diff --git a/.babelrc b/.babelrc
new file mode 100644
index 000000000..332bba6f9
--- /dev/null
+++ b/.babelrc
@@ -0,0 +1,24 @@
+{
+ "env": {
+ "es": {
+ "presets": [
+ ["@babel/preset-env", {
+ "modules": false
+ }]
+ ]
+ },
+ "development": {
+ "presets": [
+ ["@babel/preset-env", { "targets": { "node": "0.10" } }]
+ ],
+ "plugins": [
+ [
+ "add-module-exports",
+ {
+ "addDefaultProperty": true
+ }
+ ]
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/.eslintignore b/.eslintignore
deleted file mode 100644
index 4627fe57a..000000000
--- a/.eslintignore
+++ /dev/null
@@ -1,2 +0,0 @@
-validator.js
-validator.min.js
\ No newline at end of file
diff --git a/.eslintrc.json b/.eslintrc.json
index 15e302d6d..285c41e15 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -1,5 +1,6 @@
{
"extends": "airbnb-base",
+ "parser": "babel-eslint",
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "module"
@@ -11,14 +12,29 @@
"mocha": true
},
"rules": {
- "camelcase": [0],
- "no-param-reassign": [0],
+ "camelcase": 0,
+ "no-param-reassign": 0,
"one-var": 0,
"one-var-declaration-per-line": 0,
"func-names": 0,
"no-console": 0,
"newline-per-chained-call": 0,
"prefer-const": 0,
- "no-restricted-syntax": [2, "DebuggerStatement", "LabeledStatement", "WithStatement"]
+ "linebreak-style": 0,
+ "no-restricted-syntax": [2, "DebuggerStatement", "LabeledStatement", "WithStatement"],
+ "no-restricted-globals": 0,
+ "prefer-destructuring": 0,
+ "comma-dangle": [2, {
+ "arrays": "always-multiline",
+ "objects": "always-multiline",
+ "imports": "always-multiline",
+ "exports": "always-multiline",
+ "functions": "never"
+ }],
+ "no-plusplus": [2, {
+ "allowForLoopAfterthoughts": true
+ }],
+ "no-prototype-builtins": 0,
+ "no-useless-escape": 0
}
}
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 000000000..ac5ace58e
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1 @@
+open_collective: validatorjs
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 000000000..799e41348
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,23 @@
+---
+name: Bug report
+about: Create a report to help us improve
+title: ''
+labels: "\U0001F41B bug"
+assignees: ''
+
+---
+
+**Describe the bug**
+A clear and concise description of what the bug is.
+
+
+**Examples**
+If applicable, add screenshots to help explain your problem.
+
+**Reproductions**
+If applicable, provide a reproduction on platforms like [runkit](npm.runkit.com/validator)
+
+**Additional context**
+Validator.js version:
+Node.js version:
+OS platform: [windows, linux, macOS, etc]
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
new file mode 100644
index 000000000..ba6041d49
--- /dev/null
+++ b/.github/pull_request_template.md
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+## Checklist
+
+- [ ] PR contains only changes related; no stray files, etc.
+- [ ] README updated (where applicable)
+- [ ] Tests written (where applicable)
+- [ ] References provided in PR (where applicable)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 000000000..e0024eff3
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,29 @@
+name: CI
+on:
+ push:
+ branches: [master]
+ pull_request:
+ branches: [master]
+jobs:
+ test:
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ node-version: [22, 20, 18, 16, 14, 12, 10, 8]
+ name: Run tests on Node.js ${{ matrix.node-version }}
+ steps:
+ - name: Setup Node.js ${{ matrix.node-version }}
+ uses: actions/setup-node@v4
+ with:
+ node-version: ${{ matrix.node-version }}
+ - name: Checkout repository
+ uses: actions/checkout@v4
+ - name: Install dependencies
+ run: npm install --legacy-peer-deps
+ - name: Run tests
+ run: npm test
+ - if: matrix.node-version == 22
+ name: Send coverage info to Codecov
+ uses: codecov/codecov-action@v5
+ with:
+ token: ${{ secrets.CODECOV_TOKEN }}
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
new file mode 100644
index 000000000..e89a9b51e
--- /dev/null
+++ b/.github/workflows/codeql-analysis.yml
@@ -0,0 +1,70 @@
+# For most projects, this workflow file will not need changing; you simply need
+# to commit it to your repository.
+#
+# You may wish to alter this file to override the set of languages analyzed,
+# or to provide custom queries or build logic.
+#
+# ******** NOTE ********
+# We have attempted to detect the languages in your repository. Please check
+# the `language` matrix defined below to confirm you have the correct set of
+# supported CodeQL languages.
+#
+name: "CodeQL"
+
+on:
+ push:
+ branches: [ master ]
+ pull_request:
+ # The branches below must be a subset of the branches above
+ branches: [ master ]
+ schedule:
+ - cron: '38 10 * * 4'
+
+jobs:
+ analyze:
+ name: Analyze
+ runs-on: ubuntu-latest
+ permissions:
+ actions: read
+ contents: read
+ security-events: write
+
+ strategy:
+ fail-fast: false
+ matrix:
+ language: [ 'javascript' ]
+ # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
+ # Learn more about CodeQL language support at https://git.io/codeql-language-support
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ # Initializes the CodeQL tools for scanning.
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@v3
+ with:
+ languages: ${{ matrix.language }}
+ # If you wish to specify custom queries, you can do so here or in a config file.
+ # By default, queries listed here will override any specified in a config file.
+ # Prefix the list here with "+" to use these queries and those in the config file.
+ # queries: ./path/to/local/query, your-org/your-repo/queries@main
+
+ # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
+ # If this step fails, then you should remove it and run the build manually (see below)
+ - name: Autobuild
+ uses: github/codeql-action/autobuild@v3
+
+ # ℹ️ Command-line programs to run using the OS shell.
+ # 📚 https://git.io/JvXDl
+
+ # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
+ # and modify them (or add more) to build your code if your project
+ # uses a compiled language
+
+ #- run: |
+ # make bootstrap
+ # make release
+
+ - name: Perform CodeQL Analysis
+ uses: github/codeql-action/analyze@v3
diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml
new file mode 100644
index 000000000..008a86ce6
--- /dev/null
+++ b/.github/workflows/npm-publish.yml
@@ -0,0 +1,26 @@
+name: NPM Publish
+on:
+ release:
+ types: [created]
+jobs:
+ publish:
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ id-token: write
+ steps:
+ - name: Setup Node.js 22
+ uses: actions/setup-node@v4
+ with:
+ node-version: 22
+ registry-url: https://registry.npmjs.org/
+ - name: Checkout Repository
+ uses: actions/checkout@v4
+ - name: Install Dependencies
+ run: npm install --legacy-peer-deps
+ - name: Run Tests
+ run: npm test
+ - name: Publish Package to NPM Registry
+ run: npm publish --provenance
+ env:
+ NODE_AUTH_TOKEN: ${{secrets.NPM_SECRET}}
diff --git a/.gitignore b/.gitignore
index 322fbf702..86aaedee1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,13 @@
.DS_Store
node_modules
coverage
+coverage.lcov
+.nyc_output
package-lock.json
yarn.lock
+/es
+/lib
+/index.js
+validator.js
+validator.min.js
+
diff --git a/.nycrc b/.nycrc
new file mode 100644
index 000000000..6b79f893c
--- /dev/null
+++ b/.nycrc
@@ -0,0 +1,9 @@
+{
+ "reporter": [
+ "html",
+ "text-summary"
+ ],
+ "include": [
+ "src/**/*.js"
+ ]
+}
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index b5cb1994e..000000000
--- a/.travis.yml
+++ /dev/null
@@ -1,7 +0,0 @@
-sudo: false
-language: node_js
-node_js:
- - stable
- - "6"
-after_script:
- - npm run coveralls
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c8f91bca3..25192b24a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,96 +1,940 @@
+# 13.15.20
+
+### Fixes, New Locales and Enhancements
+
+- [#2556](https://github.com/validatorjs/validator.js/pull/2556) `isMobilePhone`: add `ar-QA` locale @WardKhaddour
+- [#2576](https://github.com/validatorjs/validator.js/pull/2576) `isAlpha`/`isAlphanuneric`: add Indic locales (`ta-IN`, `te-IN`, `kn-IN`, `ml-IN`, `gu-IN`, `pa-IN`, `or-IN`) @avadootharajesh
+- [#2574](https://github.com/validatorjs/validator.js/pull/2574) `isBase64`: improve padding regex @KrayzeeKev
+- [#2584](https://github.com/validatorjs/validator.js/pull/2584) `isVAT`: improve `FR` locale @iamAmer
+- [#2608](https://github.com/validatorjs/validator.js/pull/2608) `isURL`: improve protocol detection. Resolves CVE-2025-56200 @theofidry
+- **Doc fixes and others:**
+ - [#2563](https://github.com/validatorjs/validator.js/pull/2563) @stoneLeaf
+ - [#2581](https://github.com/validatorjs/validator.js/pull/2581) @camillobruni
+
+# 13.15.15
+
+### Fixes, New Locales and Enhancements
+
+- `isMobilePhone`
+ - [#2514](https://github.com/validatorjs/validator.js/pull/2514) improve `el-CY` locale @rezk2ll
+ - [#2512](https://github.com/validatorjs/validator.js/pull/2512) improve `pt-AO` locale @renaldodev
+ - [#2502](https://github.com/validatorjs/validator.js/pull/2502) improve `ar-OM` locale @tomcastro
+- [#2089](https://github.com/validatorjs/validator.js/pull/2089) `isIP`: allow usage of option object @pixelbucket-dev
+- [#2526](https://github.com/validatorjs/validator.js/pull/2526) `isPassportNumber`: improve `CA` locale @evanbechtol
+- [#2491](https://github.com/validatorjs/validator.js/pull/2491) `isBase64`: improve validation based on RFC4648 @aseyfpour
+- [#2479](https://github.com/validatorjs/validator.js/pull/2479) `isPostalCode`: improve `FR` locale @Rajput-Balram
+- [#2088](https://github.com/validatorjs/validator.js/pull/2088) `isBefore`: allow usage of option object @pixelbucket-dev
+- [#2346](https://github.com/validatorjs/validator.js/pull/2346) `isRgbColor`: allow second digit in rgba alpha value @controlol
+- [#2453](https://github.com/validatorjs/validator.js/pull/2453) `isIP`: improve IPv6 regex @ShreySinha02
+- [#2052](https://github.com/validatorjs/validator.js/pull/2052) `isPostalCode`: add `PK` locale @mateeni-dev
+- [#2529](https://github.com/validatorjs/validator.js/pull/2529) `isPostalCode`: improve `TW` locale @Crocsx
+- [#2550](https://github.com/validatorjs/validator.js/pull/2550) `isPassportNumber`: improve `US` locale @yitzchak-schechter
+- [#2553](https://github.com/validatorjs/validator.js/pull/2553) `isUUID`: add `loose` option @bc-m
+- [#2551](https://github.com/validatorjs/validator.js/pull/2551) `isPostalCode`: add `BD` locale @tanvirrb
+- [#2555](https://github.com/validatorjs/validator.js/pull/2555) `isLicensePlate`: improve `pt-PT` locale @castrosu
+- **Doc fixes and others:**
+ - [#2372](https://github.com/validatorjs/validator.js/pull/2372) @EmersonRabelo
+ - [#2538](https://github.com/validatorjs/validator.js/pull/2538) @WikiRik
+ - [#2539](https://github.com/validatorjs/validator.js/pull/2539) @WikiRik
+ - [#2540](https://github.com/validatorjs/validator.js/pull/2540) @WikiRik
+ - [#2549](https://github.com/validatorjs/validator.js/pull/2549) @WikiRik
+ - [#2537](https://github.com/validatorjs/validator.js/pull/2537) @sgress454
+
+# 13.15.0
+
+### New Features / Validators
+
+- [#2399](https://github.com/validatorjs/validator.js/pull/2399) `isISO31661Numeric` @RobinvanderVliet
+- [#2294](https://github.com/validatorjs/validator.js/pull/2294) `isULID` @arafatkn
+- [#2215](https://github.com/validatorjs/validator.js/pull/2215) `isISO15924` @xDivisionByZerox
+
+### Fixes, New Locales and Enhancements
+
+- `isMobilePhone`
+ - [#2395](https://github.com/validatorjs/validator.js/pull/2395) add `es-GT` locale @ignaciosuarezquilis
+ - [#1971](https://github.com/validatorjs/validator.js/pull/1971) improve `en-GB` locale @ihmpavel
+ - [#2359](https://github.com/validatorjs/validator.js/pull/2359) improve `uk-UA` locale @arttiger
+ - [#2350](https://github.com/validatorjs/validator.js/pull/2350) improve `ky-KG` locale @sadraliev
+ - [#2482](https://github.com/validatorjs/validator.js/pull/2482) improve `en-ZM` locale @sonikishan
+ - [#2362](https://github.com/validatorjs/validator.js/pull/2362) improve `en-GH` locale @NanaAb-116
+ - [#2500](https://github.com/validatorjs/validator.js/pull/2500) add `mk-MK` locale @eshward95
+ - [#2534](https://github.com/validatorjs/validator.js/pull/2534) improve `sq-AL` locale @nichoola
+- [#2406](https://github.com/validatorjs/validator.js/pull/2406) `isBtcAddress` support all address formats and testnets @madoke
+- [#2339](https://github.com/validatorjs/validator.js/pull/2339) `isIBAN` improve `VG` regex @ST-DDT
+- [#2332](https://github.com/validatorjs/validator.js/pull/2332) `isISO4217` update currency codes @cbodtorf
+- [#2291](https://github.com/validatorjs/validator.js/pull/2291) `isIdentityCard` add `PK` locale @Daniyal-Qureshi
+- [#2414](https://github.com/validatorjs/validator.js/pull/2414) `isEmail` fix blacklist_chars @keshavlingala
+- [#2416](https://github.com/validatorjs/validator.js/pull/2416) `isInt`/`isFloat` handle undefined and null values @Daniyal-Qureshi
+- [#2415](https://github.com/validatorjs/validator.js/pull/2415) `isPostalCode` add `CO` locale @jorgevrgs
+- [#2404](https://github.com/validatorjs/validator.js/pull/2404) `isPassportNumber` export `passportNumberLocales` @derekparnell
+- [#2029](https://github.com/validatorjs/validator.js/pull/2029) `isRgbColor` add `allowSpaces` option @a-h-i
+- [#2421](https://github.com/validatorjs/validator.js/pull/2421) `isUUID` require valid variant field and require RFC9562 UUID in version `all` @broofa
+- [#2439](https://github.com/validatorjs/validator.js/pull/2439) `isURL` add `max_allowed_length` option @pinkiesky
+- [#2437](https://github.com/validatorjs/validator.js/pull/2437) `isEmail` reject starting with double quotes @code0emperor
+- [#2333](https://github.com/validatorjs/validator.js/pull/2333) `isLicensePlate` add `en-SG` locale @Sabarinathan07
+- [#2441](https://github.com/validatorjs/validator.js/pull/2441) `normalizeEmail` add `yandex_convert_yandexru` option @AayushGH
+- [#2443](https://github.com/validatorjs/validator.js/pull/2443) `isDate` return false instead of Error in certain cases @pano9000
+- [#2474](https://github.com/validatorjs/validator.js/pull/2474) `isLength` add `discreteLengths` option @Suven-p
+- [#2481](https://github.com/validatorjs/validator.js/pull/2481) `isDate` disallow mismatching length in `strictMode` @sonikishan
+- [#2492](https://github.com/validatorjs/validator.js/pull/2492) `isISO6346` set check digit to 0 if remainder is 10 @joelcuy
+- [#2493](https://github.com/validatorjs/validator.js/pull/2493) `isPostalCode` improve `BR` locale @ticmaisdev
+- [#2494](https://github.com/validatorjs/validator.js/pull/2494) `isEmail` allow regexp in `host_whitelist` and `host_blacklist` @weikangchia
+- [#2518](https://github.com/validatorjs/validator.js/pull/2518) `isIBAN` improve `IE`/`PS` regex @Tarasz57
+- **Doc fixes and others:**
+ - [#2402](https://github.com/validatorjs/validator.js/pull/2402) @BibhushanKarki
+ - [#2394](https://github.com/validatorjs/validator.js/pull/2394) @RobinvanderVliet
+ - [#1732](https://github.com/validatorjs/validator.js/pull/1732) @alguerocode
+ - [#2413](https://github.com/validatorjs/validator.js/pull/2413) @rubiin
+ - [#2408](https://github.com/validatorjs/validator.js/pull/2408) @profnandaa
+ - [#2411](https://github.com/validatorjs/validator.js/pull/2411) @rubiin
+ - [#2325](https://github.com/validatorjs/validator.js/pull/2325) @ovarn
+ - [#2418](https://github.com/validatorjs/validator.js/pull/2418) @ihmpavel
+ - [#2323](https://github.com/validatorjs/validator.js/pull/2323) @ovarn
+ - [#2423](https://github.com/validatorjs/validator.js/pull/2423) @rubiin
+ - [#2409](https://github.com/validatorjs/validator.js/pull/2409) @profnandaa
+ - [#2442](https://github.com/validatorjs/validator.js/pull/2442) @pano9000
+
+# 13.12.0
+
+### New Features / Validators
+
+- [#2143](https://github.com/validatorjs/validator.js/pull/2143) `isAbaRouting` @songyuew
+
+### Fixes, New Locales and Enhancements
+
+- [#2207](https://github.com/validatorjs/validator.js/pull/2207) `isLicensePlate` add Pakistani `en-PK` locale @anasshakil
+- [#2208](https://github.com/validatorjs/validator.js/issues/2208) `isPort` fix invalid leading zeros @anasshakil
+- [#2224](https://github.com/validatorjs/validator.js/pull/2224) `isTaxID` added Argentina `es-AR` locale @estefrare
+- [#2257](https://github.com/validatorjs/validator.js/pull/2257) `isDate` timezone offset fix @tomaspanek
+- [#2265](https://github.com/validatorjs/validator.js/pull/2265) `isPassportNumber` added `ZA` locale @GMorris-professional
+- `isMobilePhone`:
+ - [#2267](https://github.com/validatorjs/validator.js/pull/2267) added `en-MW` locale @SimranSiddiqui
+ - [#2140](https://github.com/validatorjs/validator.js/pull/2140) fix `am-AM` locale @AlexKrupko
+- [#2271](https://github.com/validatorjs/validator.js/pull/2271) `isPostalAddress` fix `NL` locale @RobinvanderVliet
+- [#2273](https://github.com/validatorjs/validator.js/pull/2273) `isISO4217` add `SLE` currency @urg
+- [#2278](https://github.com/validatorjs/validator.js/pull/2278) `isStrongPassword` fix symbolRegex to include `\` @nandavikas
+- [#2279](https://github.com/validatorjs/validator.js/pull/2279) `isVAT` fixed `KZ` locale @MatthieuLemoine
+- [#2285](https://github.com/validatorjs/validator.js/pull/2285) `isAlpha`, `isAlphanumeric` added `eo` locale @RobinvanderVliet
+- [#2320](https://github.com/validatorjs/validator.js/pull/2320) `isIBAN` add Algeria `DZ` locale @thibault-lr
+- [#2343](https://github.com/validatorjs/validator.js/pull/2343) `isVAT`improve `AU` locale @matthewberryman
+- [#2345](https://github.com/validatorjs/validator.js/pull/2345) `isUUID` add support for v7 @ruscon
+- [#2358](https://github.com/validatorjs/validator.js/pull/2358) `isTaxID` add Ukraine `uk-UA` locale @arttiger
+- [#2381](https://github.com/validatorjs/validator.js/pull/2381) `isDate` disallow hiphen before year @Sumit-tech-joshi
+- **Doc fixes and others:**
+ - [#2276](https://github.com/validatorjs/validator.js/pull/2276) @meyfa
+ - [#2341](https://github.com/validatorjs/validator.js/pull/2341) @WikiRik
+ - [#2364](https://github.com/validatorjs/validator.js/pull/2364) @rubiin
+ - [#2368](https://github.com/validatorjs/validator.js/pull/2368) @ZhulinskiiDanil
+ - [#2371](https://github.com/validatorjs/validator.js/pull/2371) @devmanbud
+ - [#2386](https://github.com/validatorjs/validator.js/pull/2386) @alinaghale88
+
+# 13.11.0
+
+### New Features / Validators
+
+- [#2144](https://github.com/validatorjs/validator.js/pull/2144) `isFreightContainerID`: for shipping containers IDs @songyuew
+- [#2188](https://github.com/validatorjs/validator.js/pull/2188) `isMailtoURI` @uksarkar
+
+### Fixes, New Locales and Enhancements
+
+- [#2025](https://github.com/validatorjs/validator.js/pull/2025) `isIBAN` add `MA` locale @lroudge
+- [#2117](https://github.com/validatorjs/validator.js/pull/2117) `isCreditCard` refactor @pano9000
+- [#2189](https://github.com/validatorjs/validator.js/pull/2189) `isLocale` add support for more language tags @kwahome
+- [#2203](https://github.com/validatorjs/validator.js/pull/2203) `isVAT` for `CU` @jimmyorpheus
+- [#2217](https://github.com/validatorjs/validator.js/pull/2217) `isJWT` @Prathamesh061
+- [#2222](https://github.com/validatorjs/validator.js/pull/2222) `IsFQDN` test enhancements @aalekhpatel07
+- [#2226](https://github.com/validatorjs/validator.js/pull/2226) `isAlpha`, `isAlphanumeric` for `kk-KZ` @BekStar7
+- [#2229](https://github.com/validatorjs/validator.js/pull/2229) `isEmail` support `allow_underscores` @guspower
+- [#2231](https://github.com/validatorjs/validator.js/pull/2231) `isDate` enhance Date declaration compatibility across multiple environments @CiprianS
+- [#2235](https://github.com/validatorjs/validator.js/pull/2235) `isIBAN` add white and blacklist options to the isIBAN validator @edilson
+- [#2237](https://github.com/validatorjs/validator.js/pull/2237) `isEmail` do not allow non-breaking space in user part @jeremy21212121
+- `isMobilePhone`:
+ - [#2175](https://github.com/validatorjs/validator.js/pull/2175) `so-SO` @ohersi
+ - [#2176](https://github.com/validatorjs/validator.js/pull/2176) `fr-CF` @cheboi
+ - [#2197](https://github.com/validatorjs/validator.js/pull/2197) `es-CU` @klaframboise
+ - [#2202](https://github.com/validatorjs/validator.js/pull/2202) `pl-PL` @czerwony03
+ - [#2209](https://github.com/validatorjs/validator.js/pull/2209) `fr-WF` @aidos42
+ - [#2246](https://github.com/validatorjs/validator.js/pull/2246) `ar-SD` @Hussienma
+
+# 13.9.0
+
+### New Features / Validators
+
+- [#1892](https://github.com/validatorjs/validator.js/pull/1892) `isISO6391`: add ISO 639-1 validator @braaar
+- [#1974](https://github.com/validatorjs/validator.js/pull/1974) `isLuhnNumber` @ST-DDT
+
+### Fixes and Enhancements
+
+- [#1865](https://github.com/validatorjs/validator.js/pull/1865) `isMACAddress`: add EUI-validation @WikiRik @tux-tn
+- [#1888](https://github.com/validatorjs/validator.js/pull/1888) `isBase32`: add option for Crockford's base32 alternative @BigOsvaap
+- [#1916](https://github.com/validatorjs/validator.js/pull/1916) `isDataURI`: fix mediaType format @temoffey
+- [#1920](https://github.com/validatorjs/validator.js/pull/1920) `isEmail`: add `host_whitelist` option @poor-coder
+- [#1939](https://github.com/validatorjs/validator.js/pull/1939) `isFQDN`: fix `allow_numeric_tld` option @BigOsvaap
+- [#1962](https://github.com/validatorjs/validator.js/pull/1962) `isIP`: refactor @UnKnoWn-Consortium
+- [#1967](https://github.com/validatorjs/validator.js/pull/1967) `isLength` @ikkyu-3
+- [#1992](https://github.com/validatorjs/validator.js/pull/1992) `isMagnetURI` @Rhilip @tux-tn
+- [#1995](https://github.com/validatorjs/validator.js/pull/1995) `isURL`: fix check for host @mortbauer
+- [#2008](https://github.com/validatorjs/validator.js/pull/2008) `isCreditCard` @brianwhaley
+- [#2075](https://github.com/validatorjs/validator.js/pull/2075) `isAfter`: allow usage of option object @WikiRik
+- [#2114](https://github.com/validatorjs/validator.js/pull/2114) `isRgbColor` @pano9000
+- [#2122](https://github.com/validatorjs/validator.js/pull/2122) `isDataURI`: fix MIME types with underscores @pano9000
+- [#2148](https://github.com/validatorjs/validator.js/pull/2148) `isStrongPassword` @sandmule
+- [#2157](https://github.com/validatorjs/validator.js/pull/2157) `isISBN`: allow usage of option object @WikiRik
+- [#2170](https://github.com/validatorjs/validator.js/pull/2170) `isEmail`: fix `ignore_max_length` for FQDN @sakhmedbayev
+- [#2020](https://github.com/validatorjs/validator.js/pull/2170) `isFloat`: fix comma(,) passing as float @frederike-ramin
+
+- Documentation fixes:
+
+ - [#1860](https://github.com/validatorjs/validator.js/pull/1860) @leonardovillela
+ - [#1861](https://github.com/validatorjs/validator.js/pull/1860) @tux-tn
+ - [#1957](https://github.com/validatorjs/validator.js/pull/1957) @tfilo
+ - [#2010](https://github.com/validatorjs/validator.js/pull/2010) @marcelozarate
+ - [#2107](https://github.com/validatorjs/validator.js/pull/2107) @pano9000
+ - [#2160](https://github.com/validatorjs/validator.js/pull/2160) @WikiRik
+
+- Code Refactors:
+ - [#1942](https://github.com/validatorjs/validator.js/pull/1942) @CommanderRoot
+ - [#1975](https://github.com/validatorjs/validator.js/pull/1975) @fedeci
+ - [#2137](https://github.com/validatorjs/validator.js/pull/2137) [#2132](https://github.com/validatorjs/validator.js/pull/2132) @pano9000
+
+### New and Improved Locales
+
+- `isAlpha`, `isAlphanumeric`:
+
+ - [#1678](https://github.com/validatorjs/validator.js/pull/1678) `bn-BD` @rak810
+ - [#1996](https://github.com/validatorjs/validator.js/pull/1996) `si-LK` @melkorCBA
+ - [#2014](https://github.com/validatorjs/validator.js/pull/2014) `ja-JP` @starcharles
+ - [#1995](https://github.com/validatorjs/validator.js/pull/1995) `ko-KR` @Dongkyuuuu
+
+- `isBIC`:
+
+ - [#2046](https://github.com/validatorjs/validator.js/pull/2046) `XK` @import-brain
+
+- `isIdentityCard`:
+
+ - [#2142](https://github.com/validatorjs/validator.js/pull/2142) `hk-HK` @Dongkyuuuu
+
+- `isMobilePhone`:
+
+ - [#1813](https://github.com/validatorjs/validator.js/pull/1813) `my-MM`, @ferdousulhaque
+ - [#1868](https://github.com/validatorjs/validator.js/pull/1868) `de-DE`, @thomaschaaf
+ - [#1896](https://github.com/validatorjs/validator.js/pull/1896) `en-LS`, @DevilsAutumn
+ - [#1897](https://github.com/validatorjs/validator.js/pull/1897) `el-CY`, @ikerasiotis
+ - [#1909](https://github.com/validatorjs/validator.js/pull/1909) `es-NI`, @ajGingrich
+ - [#1910](https://github.com/validatorjs/validator.js/pull/1910) `az-AZ`, @shaanaliyev
+ - [#1922](https://github.com/validatorjs/validator.js/pull/1922) `ir-IR`, @ArashST79
+ - [#1924](https://github.com/validatorjs/validator.js/pull/1924) `ky-KG`, @arsalanfiroozi
+ - [#1925](https://github.com/validatorjs/validator.js/pull/1925) `ar-YE`, `ar-EH`, `fa-AF`, @Mustafiz04
+ - [#1932](https://github.com/validatorjs/validator.js/pull/1932) `ro-MD`, @mik7up
+ - [#1940](https://github.com/validatorjs/validator.js/pull/1940) `ar-YE`, `en-BS`, @savannahvaith
+ - [#1952](https://github.com/validatorjs/validator.js/pull/1952) `ka-GE`, @avkvak
+ - [#1964](https://github.com/validatorjs/validator.js/pull/1964) [#1951](https://github.com/validatorjs/validator.js/pull/1951) `pt-BR`, @jhcaiafa @matheusnascgomes
+ - [#1983](https://github.com/validatorjs/validator.js/pull/1983) `es-HN`, @ademyan05
+ - [#1985](https://github.com/validatorjs/validator.js/pull/1985) `nl-AW`, @adida948
+ - [#1986](https://github.com/validatorjs/validator.js/pull/1986) `en-JM`, @ademyan05
+ - [#1993](https://github.com/validatorjs/validator.js/pull/1993) `mn-MN`, @rksp25
+ - [#1997](https://github.com/validatorjs/validator.js/pull/1997) `fr-BJ`, @rkuma552 @rksp25
+ - [#2001](https://github.com/validatorjs/validator.js/pull/2001) `mg-MG`, @ShivangiRai1310
+ - [#2002](https://github.com/validatorjs/validator.js/pull/2002) `en-PG`, @kai2128
+ - [#2004](https://github.com/validatorjs/validator.js/pull/2004) `en-AG`, @jiaweilow
+ - [#2007](https://github.com/validatorjs/validator.js/pull/2007) `en-AI`, @elaine1129
+ - [#2011](https://github.com/validatorjs/validator.js/pull/2011) `en-KN`, @Eelyneee
+ - [#2041](https://github.com/validatorjs/validator.js/pull/2041) `fr-CD`, @coolbeatz71
+ - [#2084](https://github.com/validatorjs/validator.js/pull/2084) `en-SS`, @cheboi
+ - [#2109](https://github.com/validatorjs/validator.js/pull/2109) `dv-MV`, @pano9000
+ - [#2129](https://github.com/validatorjs/validator.js/pull/2129) `en-HN`, @WikiRik
+ - [#2148](https://github.com/validatorjs/validator.js/pull/2148) `ar-KW`, @Yazan-KE @WikiRik
+ - [#2112](https://github.com/validatorjs/validator.js/pull/2112) `el-GR`, @pano9000
+ - [#2116](https://github.com/validatorjs/validator.js/pull/2116) `en-BM`, @pano9000
+ - [#2155](https://github.com/validatorjs/validator.js/pull/2155) `ms-MY`, @pano9000
+ - [#2156](https://github.com/validatorjs/validator.js/pull/2156) `ro-RO`, @pano9000
+
+- `isLicensePlate`:
+
+ - [#1665](https://github.com/validatorjs/validator.js/pull/1665) `sv-SE`, @elmaxe
+ - [#1895](https://github.com/validatorjs/validator.js/pull/1895) `hu-HU`, @szabolcstarnai
+ - [#1944](https://github.com/validatorjs/validator.js/pull/1944) `en-NI`, @NishantJS
+ - [#1945](https://github.com/validatorjs/validator.js/pull/1945) `de-DE`, @bennetfabian
+ - [#1945](https://github.com/validatorjs/validator.js/pull/1945) `de-DE`, @bennetfabian
+ - [#2103](https://github.com/validatorjs/validator.js/pull/2103) `es-AR`, @alvarocastro
+
+- `isPassportNumber`:
+
+ - [#1515](https://github.com/validatorjs/validator.js/pull/1515) `JM`,`KZ`,`LI`,`NZ` @JuanFML
+ - [#1814](https://github.com/validatorjs/validator.js/pull/1814) `TH` @TonPC64 @braaar
+ - [#2061](https://github.com/validatorjs/validator.js/pull/2061) `AZ` @djeks922
+ - [#2073](https://github.com/validatorjs/validator.js/pull/2073) `PH`,`PK` @digambar-t7
+
+- `isPostalCode`:
+
+ - [#1951](https://github.com/validatorjs/validator.js/pull/1951) `BA`, @matheusnascgomes
+ - [#2134](https://github.com/validatorjs/validator.js/pull/2134) `BY`, @pano9000
+ - [#2136](https://github.com/validatorjs/validator.js/pull/2136) `IR`, @pano9000
+
+- `isTaxID`:
+ - [#1867](https://github.com/validatorjs/validator.js/pull/1867) `en-CA`, @boonya
+ - [#1989](https://github.com/validatorjs/validator.js/pull/1989) `'AT', 'BE', 'BG', 'HR', 'CY', 'CZ', 'DK', 'EE', 'FI', 'FR', 'DE', 'EL', 'HU', 'IE', 'LV', 'LT', 'LU', 'MT', 'PL', 'PT', 'RO', 'SK', 'SI', 'ES', 'SE', 'AL', 'MK', 'AU', 'BY', 'CA', 'IS', 'IN', 'ID', 'IL', 'KZ', 'NZ', 'NG', 'NO', 'PH', 'RU', 'SM', 'SA', 'RS', 'CH', 'TR', 'UA', 'UZ', 'AR', 'BO', 'BR', 'CL', 'CO', 'CR', 'EC', 'SV', 'GT', 'HN', 'MX', 'NI', 'PA', 'PY', 'PE', 'DO', 'UY', 'VE'` @Dev1lDragon
+
+## 13.7.0
+
+### New Features
+
+- [#1706](https://github.com/validatorjs/validator.js/pull/1706) `isISO4217`, currency code validator @jpaya17
+
+### Fixes and Enhancements
+
+- [#1647](https://github.com/validatorjs/validator.js/pull/1647) `isFQDN`: add `allow_wildcard` option @fasenderos
+- [#1654](https://github.com/validatorjs/validator.js/pull/1654) `isRFC3339`: Disallow prepended and appended strings to RFC 3339 date-time @jmacmahon
+- [#1658](https://github.com/validatorjs/validator.js/pull/1658) maintenance: increase code coverage @tux-tn
+- [#1669](https://github.com/validatorjs/validator.js/pull/1669) `IBAN` export list of country codes that implement IBAN @dror-heller @fedeci
+- [#1676](https://github.com/validatorjs/validator.js/pull/1676) `isBoolean`: add `loose` option @brybrophy
+- [#1697](https://github.com/validatorjs/validator.js/pull/1697) maintenance: fix npm installation error @rubiin
+- [#1708](https://github.com/validatorjs/validator.js/pull/1708) `isISO31661Alpha3`: perf @jpaya17
+- [#1711](https://github.com/validatorjs/validator.js/pull/1711) `isDate`: allow users to strictly validate dates with `.` as delimiter @flymans
+- [#1715](https://github.com/validatorjs/validator.js/pull/1715) `isCreditCard`: fix for Union Pay cards @shreyassai123
+- [#1718](https://github.com/validatorjs/validator.js/pull/1718) `isEmail`: replace all dots in GMail length validation @DasDingGehtNicht
+- [#1721](https://github.com/validatorjs/validator.js/pull/1721) `isURL`: add `allow_fragments` and `allow_query_components` @cowboy-bebug
+- [#1724](https://github.com/validatorjs/validator.js/pull/1724) `isISO31661Alpha2`: perf @jpaya17
+- [#1730](https://github.com/validatorjs/validator.js/pull/1730) `isMagnetURI` @tux-tn
+- [#1738](https://github.com/validatorjs/validator.js/pull/1738) `rtrim`: remove regex to prevent ReDOS attack @tux-tn
+- [#1747](https://github.com/validatorjs/validator.js/pull/1747) maintenance: run scripts in parallel for build and clean @sachinraja
+- [#1748](https://github.com/validatorjs/validator.js/pull/1748) `isURL`: higher priority to `whitelist` @deepanshu2506
+- [#1751](https://github.com/validatorjs/validator.js/pull/1751) `isURL`: allow url with colon and no port @MatteoPierro
+- [#1777](https://github.com/validatorjs/validator.js/pull/1777) `isUUID`: fix for `null` version argument @theteladras
+- [#1799](https://github.com/validatorjs/validator.js/pull/1799) `isFQDN`: check more special chars @MatteoPierro
+- [#1833](https://github.com/validatorjs/validator.js/pull/1833) `isURL`: allow URL with an empty user @MiguelSavignano
+- [#1835](https://github.com/validatorjs/validator.js/pull/1835) `unescape`: fixed bug where intermediate string contains escaped @Marcholio
+- [#1836](https://github.com/validatorjs/validator.js/pull/1836) `contains`: can check that string contains seed multiple times @Marcholio
+- [#1844](https://github.com/validatorjs/validator.js/pull/1844) docs: add CDN instructions @luiscobits
+- [#1848](https://github.com/validatorjs/validator.js/pull/1848) `isUUID`: add support for validation of `v1` and `v2` @theteladras
+- [#1941](https://github.com/validatorjs/validator.js/pull/1641) `isEmail`: add `host_blacklist` option @fedeci
+
+### New and Improved Locales
+
+- `isAlpha`, `isAlphanumeric`:
+
+ - [#1716](https://github.com/validatorjs/validator.js/pull/1716) `hi-IN` @MiKr13
+ - [#1837](https://github.com/validatorjs/validator.js/pull/1837) `fi-FI` @Marcholio
+
+- `isPassportNumber`:
+
+ - [#1656](https://github.com/validatorjs/validator.js/pull/1656) `ID` @rubiin
+ - [#1714](https://github.com/validatorjs/validator.js/pull/1714) `CN` @anirudhgiri
+ - [#1809](https://github.com/validatorjs/validator.js/pull/1809) `PL` @Ronqn
+ - [#1810](https://github.com/validatorjs/validator.js/pull/1810) `RU` @Theta-Dev
+
+- `isPostalCode`:
+
+ - [#1788](https://github.com/validatorjs/validator.js/pull/1788) `LK` @nimanthadilz
+
+- `isIdentityCard`:
+
+ - [#1657](https://github.com/validatorjs/validator.js/pull/1657) `TH` @tithanayut
+ - [#1745](https://github.com/validatorjs/validator.js/pull/1745) `PL` @wiktorwojcik112 @fedeci @tux-tn
+ - [#1786](https://github.com/validatorjs/validator.js/pull/1786) `LK` @nimanthadilz @tux-tn
+ - [#1838](https://github.com/validatorjs/validator.js/pull/1838) `FI` @Marcholio
+
+- `isMobilePhone`:
+
+ - [#1679](https://github.com/validatorjs/validator.js/pull/1679) `de-DE` @AnnaMariaJansen
+ - [#1689](https://github.com/validatorjs/validator.js/pull/1689) `vi-VN` @luisrivas
+ - [#1695](https://github.com/validatorjs/validator.js/pull/1695) [#1682](https://github.com/validatorjs/validator.js/pull/1682) `zh-CN` @laulujan @yisibl
+ - [#1734](https://github.com/validatorjs/validator.js/pull/1734) `es-VE` @islasjuanp
+ - [#1746](https://github.com/validatorjs/validator.js/pull/1746) `nl-BE` @divikshrivastava
+ - [#1765](https://github.com/validatorjs/validator.js/pull/1765) `es-CU` @pasagedev
+ - [#1766](https://github.com/validatorjs/validator.js/pull/1766) `es-SV`, @hereje
+ - [#1767](https://github.com/validatorjs/validator.js/pull/1767) `ar-PS`, @brendan-c
+ - [#1769](https://github.com/validatorjs/validator.js/pull/1769) `en-BM` @HackProAIT
+ - [#1770](https://github.com/validatorjs/validator.js/pull/1770) `dz-BT` @lakshayr003
+ - [#1771](https://github.com/validatorjs/validator.js/pull/1771) `en-BW`, @mgndolan
+ - [#1772](https://github.com/validatorjs/validator.js/pull/1772) `fr-CM` @beckettnormington
+ - [#1778](https://github.com/validatorjs/validator.js/pull/1778) `en-PK` @ammad20120 @tux-tn
+ - [#1780](https://github.com/validatorjs/validator.js/pull/1780) `tk-TM`, @Husan-Eshonqulov
+ - [#1784](https://github.com/validatorjs/validator.js/pull/1784) `en-GY`, @mfkrause
+ - [#1785](https://github.com/validatorjs/validator.js/pull/1785) `si-LK` @Madhavi96
+ - [#1797](https://github.com/validatorjs/validator.js/pull/1797) `fr-PF`, @hereje
+ - [#1820](https://github.com/validatorjs/validator.js/pull/1820) `en-KI`, @c-tanner
+ - [#1826](https://github.com/validatorjs/validator.js/pull/1826) `hu-HU` @danielTiringer
+ - [#1834](https://github.com/validatorjs/validator.js/pull/1834) `fr-BF`, `en-NA` @lakshayr003
+ - [#1846](https://github.com/validatorjs/validator.js/pull/1846) `tg-TJ` @mgnss
+
+- `isLicensePlate`:
+
+ - [#1565](https://github.com/validatorjs/validator.js/pull/1565) `cs-CZ` @filiptronicek
+ - [#1790](https://github.com/validatorjs/validator.js/pull/1790) `fi-FI` @Marcholio
+
+- `isVAT`:
+ - [#1825](https://github.com/validatorjs/validator.js/pull/1825) `NL` @zeno4ever
+
+#### 13.6.1
+
+- **New features**:
+
+ - [#1495](https://github.com/validatorjs/validator.js/pull/1495) `isLicensePlate` @firlus
+
+- **Fixes and Enhancements**:
+
+ - [#1651](https://github.com/validatorjs/validator.js/pull/1651) fix ReDOS vulnerabilities in `isHSL` and `isEmail` @tux-tn
+ - [#1644](https://github.com/validatorjs/validator.js/pull/1644) `isURL`: Allow URLs to have only a username in the userinfo subcomponent @jbuchmann-coosto
+ - [#1633](https://github.com/validatorjs/validator.js/pull/1633) `isISIN`: optimization @bmacnaughton
+ - [#1632](https://github.com/validatorjs/validator.js/pull/1632) `isIP`: improved pattern for IPv4 and IPv6 @ognjenjevremovic
+ - [#1625](https://github.com/validatorjs/validator.js/pull/1625) fix `[A-z]` regex range on some validators @bmacnaughton
+ - [#1620](https://github.com/validatorjs/validator.js/pull/1620) fix docs @prahaladbelavadi
+ - [#1616](https://github.com/validatorjs/validator.js/pull/1616) `isMacAddress`: improve regexes and options @fedeci
+ - [#1603](https://github.com/validatorjs/validator.js/pull/1603) fix ReDOS vulnerabilities in `isSlug` and `rtrim` @fedeci
+ - [#1594](https://github.com/validatorjs/validator.js/pull/1594) `isIPRange`: add support for IPv6 @neilime
+ - [#1577](https://github.com/validatorjs/validator.js/pull/1577) `isEAN`: add support for EAN-14 @varsubham @tux-tn
+ - [#1566](https://github.com/validatorjs/validator.js/pull/1566) `isStrongPassword`: add `@` as a valid symbol @stingalleman
+ - [#1548](https://github.com/validatorjs/validator.js/pull/1548) `isBtcAddress`: add base58 @ezkemboi
+ - [#1546](https://github.com/validatorjs/validator.js/pull/1546) `isFQDN`: numeric domain names @tux-tn
+
+- **New and Improved locales**:
+ - `isIdentityCard`, `isPassportNumber`:
+ - [#1595](https://github.com/validatorjs/validator.js/pull/1595) `IR` @mhf-ir @fedeci
+ - [#1583](https://github.com/validatorjs/validator.js/pull/1583) `ar-LY` @asghaier76 @tux-tn
+ - [#1574](https://github.com/validatorjs/validator.js/pull/1574) `MY` @stranger26 @tux-tn
+ - `isMobilePhone`:
+ - [#1642](https://github.com/validatorjs/validator.js/pull/1642) `zh-CN` @Akira0705
+ - [#1638](https://github.com/validatorjs/validator.js/pull/1638) `lv-LV` @AntonLukichev
+ - [#1635](https://github.com/validatorjs/validator.js/pull/1635) `en-GH` @ankorGH
+ - [#1604](https://github.com/validatorjs/validator.js/pull/1604) `mz-MZ` @salmento @tux-tn
+ - [#1575](https://github.com/validatorjs/validator.js/pull/1575) `vi-VN` @kyled7
+ - [#1573](https://github.com/validatorjs/validator.js/pull/1573) `en-SG` @liliwei25
+ - [#1554](https://github.com/validatorjs/validator.js/pull/1554) `de-CH`, `fr-CH`, `it-CH` @dinfekted
+ - [#1541](https://github.com/validatorjs/validator.js/pull/1541) [#1623](https://github.com/validatorjs/validator.js/pull/1623) `es-CO` @ezkemboi @tux-tn
+ - [#1506](https://github.com/validatorjs/validator.js/pull/1506) `ar-OM` @dev-sna
+ - [#1505](https://github.com/validatorjs/validator.js/pull/1505) `pt-AO` @AdilsonFuxe
+ - `isPostalCode`:
+ - [#1628](https://github.com/validatorjs/validator.js/pull/1628) `KR` @greatSumini
+ - `isTaxID`:
+ - [#1613](https://github.com/validatorjs/validator.js/pull/1613) `pt-BR` @mschunke
+ - [#1529](https://github.com/validatorjs/validator.js/pull/1529) `el-GR` @dspinellis
+ - `isVAT`:
+ - [#1536](https://github.com/validatorjs/validator.js/pull/1536) `IT` @fedeci
+
+#### ~~13.5.0~~ 13.5.1
+
+- **New features**:
+
+ - `isVAT` [#1463](https://github.com/validatorjs/validator.js/pull/1463) @ CodingNagger
+ - `isTaxID` [#1446](https://github.com/validatorjs/validator.js/pull/1446) @tplessas
+ - `isBase58` [#1445](https://github.com/validatorjs/validator.js/pull/1445) @ezkemboi
+ - `isStrongPassword` [#1348](https://github.com/validatorjs/validator.js/pull/1348) @door-bell
+
+- **Fixes and Enhancements**:
+
+ - [#1486](https://github.com/validatorjs/validator.js/pull/1486) `isISO8601`: add `strictSeparator` @brostone51
+ - [#1474](https://github.com/validatorjs/validator.js/pull/1474) `isFQDN`: make more strict @CristhianMotoche
+ - [#1469](https://github.com/validatorjs/validator.js/pull/1469) `isFQDN`: `allow_underscore` option @gibson042
+ - [#1449](https://github.com/validatorjs/validator.js/pull/1449) `isEmail`: character blacklisting @rubiin
+ - [#1436](https://github.com/validatorjs/validator.js/pull/1436) `isURL`: added `require_port` option @yshanli
+ - [#1435](https://github.com/validatorjs/validator.js/pull/1435) `isEmail`: respect `ignore_max_length` option @evantahler
+ - [#1402](https://github.com/validatorjs/validator.js/pull/1402) `isDate`: add strictMode and prevent mixed delimiters @tux-tn
+ - [#1286](https://github.com/validatorjs/validator.js/pull/1286) `isAlpha`: support `ignore` option @mum-never-proud
+
+- **New and Improved locales**:
+ - `isAlpha`, `isAlphanumeric`:
+ - [#1528](https://github.com/validatorjs/validator.js/pull/1528) multiple fixes @tux-tn @purell
+ - [#1513](https://github.com/validatorjs/validator.js/pull/1513) `id-ID` and docs update @bekicot
+ - [#1484](https://github.com/validatorjs/validator.js/pull/1484) [#1481](https://github.com/validatorjs/validator.js/pull/1481) `th-TH` @ipiranhaa
+ - [#1455](https://github.com/validatorjs/validator.js/pull/1455) `fa-IR` @fakhrip
+ - [#1447](https://github.com/validatorjs/validator.js/pull/1447) `az-AZ` @saidfagan
+ - `isMobilePhone`:
+ - [#1521](https://github.com/validatorjs/validator.js/pull/1521) `ar-MA` @artpumpkin
+ - [#1492](https://github.com/validatorjs/validator.js/pull/1492) `de-LU`,`it-SM`, `sq-AL` and `ga-IE` @firlus
+ - [#1487](https://github.com/validatorjs/validator.js/pull/1487) `en-HN` @jehielmartinez
+ - [#1473](https://github.com/validatorjs/validator.js/pull/1473) `ar-LB`, `es-PE`, `ka-GE` @rubiin
+ - [#1470](https://github.com/validatorjs/validator.js/pull/1444) `es-DO` @devrasec
+ - [#1460](https://github.com/validatorjs/validator.js/pull/1444) `es-BO` @rubiin
+ - [#1444](https://github.com/validatorjs/validator.js/pull/1444) `es-AR` @csrgt
+ - [#1407](https://github.com/validatorjs/validator.js/pull/1407) `pt-BR` @viniciushvsilva
+ - `isPostalCode`:
+ - [#1534](https://github.com/validatorjs/validator.js/pull/1534) `CN` @httpsbao
+ - [#1515](https://github.com/validatorjs/validator.js/pull/1515) `IR` @masoudDaliriyan
+ - [#1502](https://github.com/validatorjs/validator.js/pull/1502) `SG`, `MY` @stranger26
+ - [#1480](https://github.com/validatorjs/validator.js/pull/1480) `TH` @ipiranhaa
+ - [#1459](https://github.com/validatorjs/validator.js/pull/1456) `BY` @rubiin
+ - [#1456](https://github.com/validatorjs/validator.js/pull/1456) `DO` and `HT` @yomed
+ - `isPassportNumber`:
+ - [#1468](https://github.com/validatorjs/validator.js/pull/1468) `BY` @zenby
+ - [#1467](https://github.com/validatorjs/validator.js/pull/1467) `RU` @dkochetkov
+
+— this release is dedicated to @dbnandaa 🧒
+
+#### 13.1.17
+
+- **New features**:
+ - None
+- **Fixes and chores**:
+
+ - [#1425](https://github.com/validatorjs/validator.js/pull/1425) fix validation for _userinfo_ part for `isURL` @heanzyzabala
+ - [#1419](https://github.com/validatorjs/validator.js/pull/1419) fix `isBase32` and `isBase64` to validate empty strings properly @AberDerBart
+ - [#1408](https://github.com/validatorjs/validator.js/pull/1408) tests for `isTaxId` @dspinellis
+ - [#1397](https://github.com/validatorjs/validator.js/pull/1397) added `validate_length` option for `isURL` @tomgrossman
+ - [#1383](https://github.com/validatorjs/validator.js/pull/1383) [#1428](https://github.com/validatorjs/validator.js/pull/1428) doc typos @0xflotus @timgates42
+ - [#1376](https://github.com/validatorjs/validator.js/pull/1376) add missing tests and switch to Coverall @tux-tn
+ - [#1373](https://github.com/validatorjs/validator.js/pull/1373) improve code coverage @ezkemboi
+ - [#1357](https://github.com/validatorjs/validator.js/pull/1357) add Node v6 on build pipeline @profnandaa
+
+- **New and Improved locales**:
+ - `isMobilePhone`:
+ - [#1439](https://github.com/validatorjs/validator.js/pull/1439) `az-AZ` @saidfagan
+ - [#1420](https://github.com/validatorjs/validator.js/pull/1420) `uz-Uz` @icyice0217
+ - [#1391](https://github.com/validatorjs/validator.js/pull/1391) `de-DE` @heanzyzabala
+ - [#1388](https://github.com/validatorjs/validator.js/pull/1388) `en-PH` @stinkymonkeyph
+ - [#1370](https://github.com/validatorjs/validator.js/pull/1370) `es-ES` @rubiin
+ - [#1356](https://github.com/validatorjs/validator.js/pull/1356) `bs-BA` @MladenZeljic
+ - [#1303](https://github.com/validatorjs/validator.js/pull/1301) `zh-CN` @heathcliff-hu
+ - `isPostalCode`:
+ - [#1439](https://github.com/validatorjs/validator.js/pull/1439) `AZ` @saidfagan
+ - [#1370](https://github.com/validatorjs/validator.js/pull/1370) `ES` @rubiin
+ - [#1367](https://github.com/validatorjs/validator.js/pull/1367) `IL` @rubiin
+ - `isAlpha`, `isAlphanumeric`:
+ - [#1411](https://github.com/validatorjs/validator.js/pull/1411) `fa-AF`, `fa-IR` @stinkymonkeyph
+ - [#1371](https://github.com/validatorjs/validator.js/pull/1371) `vi-VN` @rubiin
+ - `isBAN`:
+ - [#1394](https://github.com/validatorjs/validator.js/pull/1394) `EG`, `SV` @heanzyzabala
+ - `isIdentityCard`:
+ - [#1384](https://github.com/validatorjs/validator.js/pull/1384) `IT` @lorenzodb1
+
+#### 13.1.1
+
+- Hotfix for a regex incompatibility in some browsers
+ ([#1355](https://github.com/validatorjs/validator.js/pull/1355)
+
+#### 13.1.0
+
+- Added an `isIMEI()` validator
+ ([#1346](https://github.com/validatorjs/validator.js/pull/1346))
+- Added an `isDate()` validator
+ ([#1270](https://github.com/validatorjs/validator.js/pull/1270))
+- Added an `isTaxID()` validator
+ ([#1336](https://github.com/validatorjs/validator.js/pull/1336))
+- Added DMS support to `isLatLong()`
+ ([#1340](https://github.com/validatorjs/validator.js/pull/1340))
+- Added support for URL-safe base64 validation
+ ([#1277](https://github.com/validatorjs/validator.js/pull/1277))
+- Added support for primitives in `isJSON()`
+ ([#1328](https://github.com/validatorjs/validator.js/pull/1328))
+- Added support for case-insensitive matching to `contains()`
+ ([#1334](https://github.com/validatorjs/validator.js/pull/1334))
+- Support additional cards in `isCreditCard()`
+ ([#1177](https://github.com/validatorjs/validator.js/pull/1177))
+- Support additional currencies in `isCurrency()`
+ ([#1306](https://github.com/validatorjs/validator.js/pull/1306))
+- Fixed `isFQDN()` handling of certain special chars
+ ([#1091](https://github.com/validatorjs/validator.js/pull/1091))
+- Fixed a bug in `isSlug()`
+ ([#1338](https://github.com/validatorjs/validator.js/pull/1338))
+- New and improved locales
+ ([#1112](https://github.com/validatorjs/validator.js/pull/1112),
+ [#1167](https://github.com/validatorjs/validator.js/pull/1167),
+ [#1198](https://github.com/validatorjs/validator.js/pull/1198),
+ [#1199](https://github.com/validatorjs/validator.js/pull/1199),
+ [#1273](https://github.com/validatorjs/validator.js/pull/1273),
+ [#1279](https://github.com/validatorjs/validator.js/pull/1279),
+ [#1281](https://github.com/validatorjs/validator.js/pull/1281),
+ [#1293](https://github.com/validatorjs/validator.js/pull/1293),
+ [#1294](https://github.com/validatorjs/validator.js/pull/1294),
+ [#1311](https://github.com/validatorjs/validator.js/pull/1311),
+ [#1312](https://github.com/validatorjs/validator.js/pull/1312),
+ [#1313](https://github.com/validatorjs/validator.js/pull/1313),
+ [#1314](https://github.com/validatorjs/validator.js/pull/1314),
+ [#1315](https://github.com/validatorjs/validator.js/pull/1315),
+ [#1317](https://github.com/validatorjs/validator.js/pull/1317),
+ [#1322](https://github.com/validatorjs/validator.js/pull/1322),
+ [#1324](https://github.com/validatorjs/validator.js/pull/1324),
+ [#1330](https://github.com/validatorjs/validator.js/pull/1330),
+ [#1337](https://github.com/validatorjs/validator.js/pull/1337))
+
+#### 13.0.0
+
+- Added `isEthereumAddress()` validator
+ to validate [Ethereum addresses](https://en.wikipedia.org/wiki/Ethereum#Addresses)
+ ([#1117](https://github.com/validatorjs/validator.js/pull/1117))
+- Added `isBtcAddress()` validator
+ to validate [Bitcoin addresses](https://en.bitcoin.it/wiki/Address)
+ ([#1163](https://github.com/validatorjs/validator.js/pull/1163))
+- Added `isIBAN()` validator
+ to validate [International Bank Account Numbers](https://en.wikipedia.org/wiki/International_Bank_Account_Number)
+ ([#1243](https://github.com/validatorjs/validator.js/pull/1243))
+- Added `isEAN()` validator
+ to validate [International Article Numbers](https://en.wikipedia.org/wiki/International_Article_Number)
+ ([#1244](https://github.com/validatorjs/validator.js/pull/1244))
+- Added `isSemVer()` validator
+ to validate [Semantic Version Numbers](https://semver.org)
+ ([#1246](https://github.com/validatorjs/validator.js/pull/1246))
+- Added `isPassportNumber()` validator
+ ([#1250](https://github.com/validatorjs/validator.js/pull/1250))
+- Added `isRgbColor()` validator
+ ([#1141](https://github.com/validatorjs/validator.js/pull/1141))
+- Added `isHSL()` validator
+ ([#1159](https://github.com/validatorjs/validator.js/pull/1159))
+- Added `isLocale()` validator
+ ([#1072](https://github.com/validatorjs/validator.js/pull/1072))
+- Improved the `isIP()` validator
+ ([#1211](https://github.com/validatorjs/validator.js/pull/1211))
+- Improved the `isMACAddress()` validator
+ ([#1267](https://github.com/validatorjs/validator.js/pull/1267))
+- New and improved locales
+ ([#1238](https://github.com/validatorjs/validator.js/pull/1238),
+ [#1265](https://github.com/validatorjs/validator.js/pull/1265))
+
+#### 12.2.0
+
+- Support CSS Colors Level 4 spec
+ ([#1233](https://github.com/validatorjs/validator.js/pull/1233))
+- Improve the `toFloat()` sanitizer
+ ([#1227](https://github.com/validatorjs/validator.js/pull/1227))
+- New and improved locales
+ ([#1200](https://github.com/validatorjs/validator.js/pull/1200),
+ [#1207](https://github.com/validatorjs/validator.js/pull/1207),
+ [#1213](https://github.com/validatorjs/validator.js/pull/1213),
+ [#1217](https://github.com/validatorjs/validator.js/pull/1217),
+ [#1234](https://github.com/validatorjs/validator.js/pull/1234))
+
+#### 12.1.0
+
+- ES module for webpack tree shaking
+ ([#1015](https://github.com/validatorjs/validator.js/pull/1015))
+- Updated `isIP()` to accept scoped IPv6 addresses
+ ([#1160](https://github.com/validatorjs/validator.js/pull/1160))
+- New and improved locales
+ ([#1162](https://github.com/validatorjs/validator.js/pull/1162),
+ [#1183](https://github.com/validatorjs/validator.js/pull/1183),
+ [#1187](https://github.com/validatorjs/validator.js/pull/1187),
+ [#1191](https://github.com/validatorjs/validator.js/pull/1191))
+
+#### 12.0.0
+
+- Added `isOctal()` validator
+ ([#1153](https://github.com/validatorjs/validator.js/pull/1153))
+- Added `isSlug()` validator
+ ([#1096](https://github.com/validatorjs/validator.js/pull/1096))
+- Added `isBIC()` validator for bank identification codes
+ ([#1071](https://github.com/validatorjs/validator.js/pull/1071))
+- Allow uppercase chars in `isHash()`
+ ([#1062](https://github.com/validatorjs/validator.js/pull/1062))
+- Allow additional prefixes in `isHexadecimal()`
+ ([#1147](https://github.com/validatorjs/validator.js/pull/1147))
+- Allow additional separators in `isMACAddress()`
+ ([#1065](https://github.com/validatorjs/validator.js/pull/1065))
+- Better defaults for `isLength()`
+ ([#1070](https://github.com/validatorjs/validator.js/pull/1070))
+- Bug fixes
+ ([#1074](https://github.com/validatorjs/validator.js/pull/1074))
+- New and improved locales
+ ([#1059](https://github.com/validatorjs/validator.js/pull/1059),
+ [#1060](https://github.com/validatorjs/validator.js/pull/1060),
+ [#1069](https://github.com/validatorjs/validator.js/pull/1069),
+ [#1073](https://github.com/validatorjs/validator.js/pull/1073),
+ [#1082](https://github.com/validatorjs/validator.js/pull/1082),
+ [#1092](https://github.com/validatorjs/validator.js/pull/1092),
+ [#1121](https://github.com/validatorjs/validator.js/pull/1121),
+ [#1125](https://github.com/validatorjs/validator.js/pull/1125),
+ [#1132](https://github.com/validatorjs/validator.js/pull/1132),
+ [#1152](https://github.com/validatorjs/validator.js/pull/1152),
+ [#1165](https://github.com/validatorjs/validator.js/pull/1165),
+ [#1166](https://github.com/validatorjs/validator.js/pull/1166),
+ [#1174](https://github.com/validatorjs/validator.js/pull/1174))
+
+#### 11.1.0
+
+- Code coverage improvements
+ ([#1024](https://github.com/validatorjs/validator.js/pull/1024))
+- New and improved locales
+ ([#1035](https://github.com/validatorjs/validator.js/pull/1035),
+ [#1040](https://github.com/validatorjs/validator.js/pull/1040),
+ [#1041](https://github.com/validatorjs/validator.js/pull/1041),
+ [#1048](https://github.com/validatorjs/validator.js/pull/1048),
+ [#1049](https://github.com/validatorjs/validator.js/pull/1049),
+ [#1052](https://github.com/validatorjs/validator.js/pull/1052),
+ [#1054](https://github.com/validatorjs/validator.js/pull/1054),
+ [#1055](https://github.com/validatorjs/validator.js/pull/1055),
+ [#1056](https://github.com/validatorjs/validator.js/pull/1056),
+ [#1057](https://github.com/validatorjs/validator.js/pull/1057))
+
+#### 11.0.0
+
+- Added a `isBase32()` validator
+ ([#1023](https://github.com/validatorjs/validator.js/pull/1023))
+- Updated `isEmail()` to validate display names according to RFC2822
+ ([#1004](https://github.com/validatorjs/validator.js/pull/1004))
+- Updated `isEmail()` to check total email length
+ ([#1007](https://github.com/validatorjs/validator.js/pull/1007))
+- The internal `toString()` util is no longer exported
+ ([0277eb](https://github.com/validatorjs/validator.js/commit/0277eb00d245a3479af52adf7d927d4036895650))
+- New and improved locales
+ ([#999](https://github.com/validatorjs/validator.js/pull/999),
+ [#1010](https://github.com/validatorjs/validator.js/pull/1010),
+ [#1017](https://github.com/validatorjs/validator.js/pull/1017),
+ [#1022](https://github.com/validatorjs/validator.js/pull/1022),
+ [#1031](https://github.com/validatorjs/validator.js/pull/1031),
+ [#1032](https://github.com/validatorjs/validator.js/pull/1032))
+
+#### 10.11.0
+
+- Fix imports like `import .. from "validator/lib/.."`
+ ([#961](https://github.com/validatorjs/validator.js/pull/961))
+- New locale
+ ([#958](https://github.com/validatorjs/validator.js/pull/958))
+
+#### 10.10.0
+
+- `isISO8601()` strict mode now works in the browser
+ ([#932](https://github.com/validatorjs/validator.js/pull/932))
+- New and improved locales
+ ([#931](https://github.com/validatorjs/validator.js/pull/931),
+ [#933](https://github.com/validatorjs/validator.js/pull/933),
+ [#947](https://github.com/validatorjs/validator.js/pull/947),
+ [#950](https://github.com/validatorjs/validator.js/pull/950))
+
+#### 10.9.0
+
+- Added an option to `isURL()` to reject email-like URLs
+ ([#901](https://github.com/validatorjs/validator.js/pull/901))
+- Added a `strict` option to `isISO8601()`
+ ([#910](https://github.com/validatorjs/validator.js/pull/910))
+- Relaxed `isJWT()` signature requirements
+ ([#906](https://github.com/validatorjs/validator.js/pull/906))
+- New and improved locales
+ ([#899](https://github.com/validatorjs/validator.js/pull/899),
+ [#904](https://github.com/validatorjs/validator.js/pull/904),
+ [#913](https://github.com/validatorjs/validator.js/pull/913),
+ [#916](https://github.com/validatorjs/validator.js/pull/916),
+ [#925](https://github.com/validatorjs/validator.js/pull/925),
+ [#928](https://github.com/validatorjs/validator.js/pull/928))
+
+#### 10.8.0
+
+- Added `isIdentityCard()`
+ ([#846](https://github.com/validatorjs/validator.js/pull/846))
+- Better error when validators are passed an invalid type
+ ([#895](https://github.com/validatorjs/validator.js/pull/895))
+- Locales are now exported
+ ([#890](https://github.com/validatorjs/validator.js/pull/890),
+ [#892](https://github.com/validatorjs/validator.js/pull/892))
+- New locale
+ ([#896](https://github.com/validatorjs/validator.js/pull/896))
+
+#### 10.7.1
+
+- Ignore case when checking URL protocol
+ ([#887](https://github.com/validatorjs/validator.js/issues/887))
+- Locale fix
+ ([#889](https://github.com/validatorjs/validator.js/pull/889))
+
+#### 10.7.0
+
+- Added `isMagnetURI()` to validate [magnet URIs](https://en.wikipedia.org/wiki/Magnet_URI_scheme)
+ ([#884](https://github.com/validatorjs/validator.js/pull/884))
+- Added `isJWT()` to validate [JSON web tokens](https://en.wikipedia.org/wiki/JSON_Web_Token)
+ ([#885](https://github.com/validatorjs/validator.js/pull/885))
+
+#### 10.6.0
+
+- Updated `isMobilePhone()` to match any locale's pattern by default
+ ([#874](https://github.com/validatorjs/validator.js/pull/874))
+- Added an option to ignore whitespace in `isEmpty()`
+ ([#880](https://github.com/validatorjs/validator.js/pull/880))
+- New and improved locales
+ ([#878](https://github.com/validatorjs/validator.js/pull/878),
+ [#879](https://github.com/validatorjs/validator.js/pull/879))
+
+#### 10.5.0
+
+- Disabled domain-specific email validation
+ ([#873](https://github.com/validatorjs/validator.js/pull/873))
+- Added support for IP hostnames in `isEmail()`
+ ([#845](https://github.com/validatorjs/validator.js/pull/845))
+- Added a `no_symbols` option to `isNumeric()`
+ ([#848](https://github.com/validatorjs/validator.js/pull/848))
+- Added a `no_colons` option to `isMACAddress()`
+ ([#849](https://github.com/validatorjs/validator.js/pull/849))
+- Updated `isURL()` to reject protocol relative URLs unless a flag is set
+ ([#860](https://github.com/validatorjs/validator.js/issues/860))
+- New and improved locales
+ ([#801](https://github.com/validatorjs/validator.js/pull/801),
+ [#856](https://github.com/validatorjs/validator.js/pull/856),
+ [#859](https://github.com/validatorjs/validator.js/issues/859),
+ [#861](https://github.com/validatorjs/validator.js/pull/861),
+ [#862](https://github.com/validatorjs/validator.js/pull/862),
+ [#863](https://github.com/validatorjs/validator.js/pull/863),
+ [#864](https://github.com/validatorjs/validator.js/pull/864),
+ [#870](https://github.com/validatorjs/validator.js/pull/870),
+ [#872](https://github.com/validatorjs/validator.js/pull/872))
+
+#### 10.4.0
+
+- Added an `isIPRange()` validator
+ ([#842](https://github.com/validatorjs/validator.js/pull/842))
+- Accept an array of locales in `isMobilePhone()`
+ ([#742](https://github.com/validatorjs/validator.js/pull/742))
+- New locale
+ ([#843](https://github.com/validatorjs/validator.js/pull/843))
+
+#### 10.3.0
+
+- Strict Gmail validation in `isEmail()`
+ ([#832](https://github.com/validatorjs/validator.js/pull/832))
+- New locales
+ ([#831](https://github.com/validatorjs/validator.js/pull/831),
+ [#835](https://github.com/validatorjs/validator.js/pull/835),
+ [#836](https://github.com/validatorjs/validator.js/pull/836))
+
+#### 10.2.0
+
+- Export the list of supported locales in `isPostalCode()`
+ ([#830](https://github.com/validatorjs/validator.js/pull/830))
+
+#### 10.1.0
+
+- Added an `isISO31661Alpha3()` validator
+ ([#809](https://github.com/validatorjs/validator.js/pull/809))
+
+#### 10.0.0
+
+- Allow floating points in `isNumeric()`
+ ([#810](https://github.com/validatorjs/validator.js/pull/810))
+- Disallow GMail addresses with multiple consecutive dots, or leading/trailing dots
+ ([#820](https://github.com/validatorjs/validator.js/pull/820))
+- Added an `isRFC3339()` validator
+ ([#816](https://github.com/validatorjs/validator.js/pull/816))
+- Reject domain parts longer than 63 octets in `isFQDN()`, `isURL()` and `isEmail()`
+ ([bb3e542](https://github.com/validatorjs/validator.js/commit/bb3e542))
+- Added a new Amex prefix to `isCreditCard()`
+ ([#805](https://github.com/validatorjs/validator.js/pull/805))
+- Fixed `isFloat()` min/max/gt/lt filters when a locale with a comma decimal is used
+ ([2b70821](https://github.com/validatorjs/validator.js/commit/2b70821))
+- Normalize Yandex emails
+ ([#807](https://github.com/validatorjs/validator.js/pull/807))
+- New locales
+ ([#803](https://github.com/validatorjs/validator.js/pull/803))
+
+#### 9.4.1
+
+- Patched a [REDOS](https://en.wikipedia.org/wiki/ReDoS) vulnerability in `isDataURI`
+- New and improved locales
+ ([#788](https://github.com/validatorjs/validator.js/pull/788))
+
+#### 9.4.0
+
+- Added an option to `isMobilePhone` to require a country code
+ ([#769](https://github.com/validatorjs/validator.js/pull/769))
+- New and improved locales
+ ([#785](https://github.com/validatorjs/validator.js/pull/785))
+
+#### 9.3.0
+
+- New and improved locales
+ ([#763](https://github.com/validatorjs/validator.js/pull/763),
+ [#768](https://github.com/validatorjs/validator.js/pull/768),
+ [#774](https://github.com/validatorjs/validator.js/pull/774),
+ [#777](https://github.com/validatorjs/validator.js/pull/777),
+ [#779](https://github.com/validatorjs/validator.js/pull/779))
+
+#### 9.2.0
+
+- Added an `isMimeType()` validator
+ ([#760](https://github.com/validatorjs/validator.js/pull/760))
+- New and improved locales
+ ([#753](https://github.com/validatorjs/validator.js/pull/753),
+ [#755](https://github.com/validatorjs/validator.js/pull/755),
+ [#764](https://github.com/validatorjs/validator.js/pull/764))
+
#### 9.1.2
- Fixed a bug with the `isFloat` validator
- ([#752](https://github.com/chriso/validator.js/pull/752))
+ ([#752](https://github.com/validatorjs/validator.js/pull/752))
#### 9.1.1
- Locale fixes
- ([#738](https://github.com/chriso/validator.js/pull/738),
- [#739](https://github.com/chriso/validator.js/pull/739))
+ ([#738](https://github.com/validatorjs/validator.js/pull/738),
+ [#739](https://github.com/validatorjs/validator.js/pull/739))
#### 9.1.0
- Added an `isISO31661Alpha2()` validator
- ([#734](https://github.com/chriso/validator.js/pull/734))
+ ([#734](https://github.com/validatorjs/validator.js/pull/734))
- New locales
- ([#735](https://github.com/chriso/validator.js/pull/735),
- [#737](https://github.com/chriso/validator.js/pull/737))
+ ([#735](https://github.com/validatorjs/validator.js/pull/735),
+ [#737](https://github.com/validatorjs/validator.js/pull/737))
#### 9.0.0
- `normalizeEmail()` no longer validates the email address
- ([#725](https://github.com/chriso/validator.js/pull/725))
+ ([#725](https://github.com/validatorjs/validator.js/pull/725))
- Added locale-aware validation to `isFloat()` and `isDecimal()`
- ([#721](https://github.com/chriso/validator.js/pull/721))
+ ([#721](https://github.com/validatorjs/validator.js/pull/721))
- Added an `isPort()` validator
- ([#733](https://github.com/chriso/validator.js/pull/733))
+ ([#733](https://github.com/validatorjs/validator.js/pull/733))
- New locales
- ([#731](https://github.com/chriso/validator.js/pull/731))
+ ([#731](https://github.com/validatorjs/validator.js/pull/731))
#### 8.2.0
- Added an `isHash()` validator
- ([#711](https://github.com/chriso/validator.js/pull/711))
+ ([#711](https://github.com/validatorjs/validator.js/pull/711))
- Control decimal places in `isCurrency()`
- ([#713](https://github.com/chriso/validator.js/pull/713))
+ ([#713](https://github.com/validatorjs/validator.js/pull/713))
- New and improved locales
- ([#700](https://github.com/chriso/validator.js/pull/700),
- [#701](https://github.com/chriso/validator.js/pull/701),
- [#714](https://github.com/chriso/validator.js/pull/714),
- [#715](https://github.com/chriso/validator.js/pull/715),
- [#718](https://github.com/chriso/validator.js/pull/718))
+ ([#700](https://github.com/validatorjs/validator.js/pull/700),
+ [#701](https://github.com/validatorjs/validator.js/pull/701),
+ [#714](https://github.com/validatorjs/validator.js/pull/714),
+ [#715](https://github.com/validatorjs/validator.js/pull/715),
+ [#718](https://github.com/validatorjs/validator.js/pull/718))
#### 8.1.0
- Fix `require('validator/lib/isIS8601')` calls
- ([#688](https://github.com/chriso/validator.js/issues/688))
+ ([#688](https://github.com/validatorjs/validator.js/issues/688))
- Added an `isLatLong()` and `isPostalCode()` validator
- ([#684](https://github.com/chriso/validator.js/pull/684))
+ ([#684](https://github.com/validatorjs/validator.js/pull/684))
- Allow comma in email display names
- ([#692](https://github.com/chriso/validator.js/pull/692))
+ ([#692](https://github.com/validatorjs/validator.js/pull/692))
- Add missing string to `unescape()`
- ([#690](https://github.com/chriso/validator.js/pull/690))
+ ([#690](https://github.com/validatorjs/validator.js/pull/690))
- Fix `isMobilePhone()` with Node <= 6.x
- ([#681](https://github.com/chriso/validator.js/issues/681))
+ ([#681](https://github.com/validatorjs/validator.js/issues/681))
- New locales
- ([#695](https://github.com/chriso/validator.js/pull/695))
+ ([#695](https://github.com/validatorjs/validator.js/pull/695))
#### 8.0.0
- `isURL()` now requires the `require_tld: false` option to validate `localhost`
- ([#675](https://github.com/chriso/validator.js/issues/675))
+ ([#675](https://github.com/validatorjs/validator.js/issues/675))
- `isURL()` now rejects URLs that are protocol only
- ([#642](https://github.com/chriso/validator.js/issues/642))
+ ([#642](https://github.com/validatorjs/validator.js/issues/642))
- Fixed a bug where `isMobilePhone()` would silently return false if the locale was invalid or unsupported
- ([#657](https://github.com/chriso/validator.js/issues/657))
+ ([#657](https://github.com/validatorjs/validator.js/issues/657))
#### 7.2.0
- Added an option to validate any phone locale
- ([#663](https://github.com/chriso/validator.js/pull/663))
+ ([#663](https://github.com/validatorjs/validator.js/pull/663))
- Fixed a bug in credit card validation
- ([#672](https://github.com/chriso/validator.js/pull/672))
+ ([#672](https://github.com/validatorjs/validator.js/pull/672))
- Disallow whitespace, including unicode whitespace, in TLDs
- ([#677](https://github.com/chriso/validator.js/pull/677))
+ ([#677](https://github.com/validatorjs/validator.js/pull/677))
- New locales
- ([#673](https://github.com/chriso/validator.js/pull/673),
- [#676](https://github.com/chriso/validator.js/pull/676))
+ ([#673](https://github.com/validatorjs/validator.js/pull/673),
+ [#676](https://github.com/validatorjs/validator.js/pull/676))
#### 7.1.0
- Added an `isISRC()` validator for [ISRC](https://en.wikipedia.org/wiki/International_Standard_Recording_Code)
- ([#660](https://github.com/chriso/validator.js/pull/660))
+ ([#660](https://github.com/validatorjs/validator.js/pull/660))
- Fixed a bug in credit card validation
- ([#670](https://github.com/chriso/validator.js/pull/670))
+ ([#670](https://github.com/validatorjs/validator.js/pull/670))
- Reduced the maximum allowed address in `isEmail()` based on
[RFC3696 errata](http://www.rfc-editor.org/errata_search.php?rfc=3696&eid=1690)
- ([#655](https://github.com/chriso/validator.js/issues/655))
+ ([#655](https://github.com/validatorjs/validator.js/issues/655))
- New locales
- ([#647](https://github.com/chriso/validator.js/pull/647),
- [#667](https://github.com/chriso/validator.js/pull/667),
- [#667](https://github.com/chriso/validator.js/pull/667),
- [#671](https://github.com/chriso/validator.js/pull/671))
+ ([#647](https://github.com/validatorjs/validator.js/pull/647),
+ [#667](https://github.com/validatorjs/validator.js/pull/667),
+ [#667](https://github.com/validatorjs/validator.js/pull/667),
+ [#671](https://github.com/validatorjs/validator.js/pull/671))
#### 7.0.0
@@ -99,290 +943,290 @@
#### 6.3.0
- Allow values like `-.01` in `isFloat()`
- ([#618](https://github.com/chriso/validator.js/issues/618))
+ ([#618](https://github.com/validatorjs/validator.js/issues/618))
- New locales
- ([#616](https://github.com/chriso/validator.js/pull/616),
- [#622](https://github.com/chriso/validator.js/pull/622),
- [#627](https://github.com/chriso/validator.js/pull/627),
- [#630](https://github.com/chriso/validator.js/pull/630))
+ ([#616](https://github.com/validatorjs/validator.js/pull/616),
+ [#622](https://github.com/validatorjs/validator.js/pull/622),
+ [#627](https://github.com/validatorjs/validator.js/pull/627),
+ [#630](https://github.com/validatorjs/validator.js/pull/630))
#### 6.2.1
- Disallow `<` and `>` in URLs
- ([#613](https://github.com/chriso/validator.js/issues/613))
+ ([#613](https://github.com/validatorjs/validator.js/issues/613))
- New locales
- ([#610](https://github.com/chriso/validator.js/pull/610))
+ ([#610](https://github.com/validatorjs/validator.js/pull/610))
#### 6.2.0
- Added an option to require an email display name
- ([#607](https://github.com/chriso/validator.js/pull/607))
+ ([#607](https://github.com/validatorjs/validator.js/pull/607))
- Added support for `lt` and `gt` to `isInt()`
- ([#588](https://github.com/chriso/validator.js/pull/588))
+ ([#588](https://github.com/validatorjs/validator.js/pull/588))
- New locales
- ([#601](https://github.com/chriso/validator.js/pull/601))
+ ([#601](https://github.com/validatorjs/validator.js/pull/601))
#### 6.1.0
- Added support for greater or less than in `isFloat()`
- ([#544](https://github.com/chriso/validator.js/issues/544))
+ ([#544](https://github.com/validatorjs/validator.js/issues/544))
- Added support for ISSN validation via `isISSN()`
- ([#593](https://github.com/chriso/validator.js/pull/593))
+ ([#593](https://github.com/validatorjs/validator.js/pull/593))
- Fixed a bug in `normalizeEmail()`
- ([#594](https://github.com/chriso/validator.js/issues/594))
+ ([#594](https://github.com/validatorjs/validator.js/issues/594))
- New locales
- ([#585](https://github.com/chriso/validator.js/pull/585))
+ ([#585](https://github.com/validatorjs/validator.js/pull/585))
#### 6.0.0
- Renamed `isNull()` to `isEmpty()`
- ([#574](https://github.com/chriso/validator.js/issues/574))
+ ([#574](https://github.com/validatorjs/validator.js/issues/574))
- Backslash is now escaped in `escape()`
- ([#516](https://github.com/chriso/validator.js/issues/516))
+ ([#516](https://github.com/validatorjs/validator.js/issues/516))
- Improved `normalizeEmail()`
- ([#583](https://github.com/chriso/validator.js/pull/583))
+ ([#583](https://github.com/validatorjs/validator.js/pull/583))
- Allow leading zeroes by default in `isInt()`
- ([#532](https://github.com/chriso/validator.js/pull/532))
+ ([#532](https://github.com/validatorjs/validator.js/pull/532))
#### 5.7.0
- Added support for IPv6 in `isURL()`
- ([#564](https://github.com/chriso/validator.js/issues/564))
+ ([#564](https://github.com/validatorjs/validator.js/issues/564))
- Added support for urls without a host (e.g. `file:///foo.txt`) in `isURL()`
- ([#563](https://github.com/chriso/validator.js/issues/563))
+ ([#563](https://github.com/validatorjs/validator.js/issues/563))
- Added support for regular expressions in the `isURL()` host whitelist and blacklist
- ([#562](https://github.com/chriso/validator.js/issues/562))
+ ([#562](https://github.com/validatorjs/validator.js/issues/562))
- Added support for MasterCard 2-Series BIN
- ([#576](https://github.com/chriso/validator.js/pull/576))
+ ([#576](https://github.com/validatorjs/validator.js/pull/576))
- New locales
- ([#575](https://github.com/chriso/validator.js/pull/575),
- [#552](https://github.com/chriso/validator.js/issues/552))
+ ([#575](https://github.com/validatorjs/validator.js/pull/575),
+ [#552](https://github.com/validatorjs/validator.js/issues/552))
#### 5.6.0
- Added an `isMD5()` validator
- ([#557](https://github.com/chriso/validator.js/pull/557))
+ ([#557](https://github.com/validatorjs/validator.js/pull/557))
- Fixed an exceptional case in `isDate()`
- ([#566](https://github.com/chriso/validator.js/pull/566))
+ ([#566](https://github.com/validatorjs/validator.js/pull/566))
- New locales
- ([#559](https://github.com/chriso/validator.js/pull/559),
- [#568](https://github.com/chriso/validator.js/pull/568),
- [#571](https://github.com/chriso/validator.js/pull/571),
- [#573](https://github.com/chriso/validator.js/pull/573))
+ ([#559](https://github.com/validatorjs/validator.js/pull/559),
+ [#568](https://github.com/validatorjs/validator.js/pull/568),
+ [#571](https://github.com/validatorjs/validator.js/pull/571),
+ [#573](https://github.com/validatorjs/validator.js/pull/573))
#### 5.5.0
- Fixed a regex denial of service in `trim()` and `rtrim()`
- ([#556](https://github.com/chriso/validator.js/pull/556))
+ ([#556](https://github.com/validatorjs/validator.js/pull/556))
- Added an Algerian locale to `isMobilePhone()`
- ([#540](https://github.com/chriso/validator.js/pull/540))
+ ([#540](https://github.com/validatorjs/validator.js/pull/540))
- Fixed the Hungarian locale in `isAlpha()` and `isAlphanumeric()`
- ([#541](https://github.com/chriso/validator.js/pull/541))
+ ([#541](https://github.com/validatorjs/validator.js/pull/541))
- Added a Polish locale to `isMobilePhone()`
- ([#545](https://github.com/chriso/validator.js/pull/545))
+ ([#545](https://github.com/validatorjs/validator.js/pull/545))
#### 5.4.0
- Accept Union Pay credit cards in `isCreditCard()`
- ([#539](https://github.com/chriso/validator.js/pull/539))
+ ([#539](https://github.com/validatorjs/validator.js/pull/539))
- Added Danish locale to `isMobilePhone()`
- ([#538](https://github.com/chriso/validator.js/pull/538))
+ ([#538](https://github.com/validatorjs/validator.js/pull/538))
- Added Hungarian locales to `isAlpha()`, `isAlphanumeric()` and `isMobilePhone()`
- ([#537](https://github.com/chriso/validator.js/pull/537))
+ ([#537](https://github.com/validatorjs/validator.js/pull/537))
#### 5.3.0
- Added an `allow_leading_zeroes` option to `isInt()`
- ([#532](https://github.com/chriso/validator.js/pull/532))
+ ([#532](https://github.com/validatorjs/validator.js/pull/532))
- Adjust Chinese mobile phone validation
- ([#523](https://github.com/chriso/validator.js/pull/523))
+ ([#523](https://github.com/validatorjs/validator.js/pull/523))
- Added a Canadian locale to `isMobilePhone()`
- ([#524](https://github.com/chriso/validator.js/issues/524))
+ ([#524](https://github.com/validatorjs/validator.js/issues/524))
#### 5.2.0
- Added a `isDataURI()` validator
- ([#521](https://github.com/chriso/validator.js/pull/521))
+ ([#521](https://github.com/validatorjs/validator.js/pull/521))
- Added Czech locales
- ([#522](https://github.com/chriso/validator.js/pull/522))
+ ([#522](https://github.com/validatorjs/validator.js/pull/522))
- Fixed a bug with `isURL()` when protocol was missing and "://" appeared in the query
- ([#518](https://github.com/chriso/validator.js/issues/518))
+ ([#518](https://github.com/validatorjs/validator.js/issues/518))
#### 5.1.0
- Added a `unescape()` HTML function
- ([#509](https://github.com/chriso/validator.js/pull/509))
+ ([#509](https://github.com/validatorjs/validator.js/pull/509))
- Added a Malaysian locale to `isMobilePhone()`
- ([#507](https://github.com/chriso/validator.js/pull/507))
+ ([#507](https://github.com/validatorjs/validator.js/pull/507))
- Added Polish locales to `isAlpha()` and `isAlphanumeric()`
- ([#506](https://github.com/chriso/validator.js/pull/506))
+ ([#506](https://github.com/validatorjs/validator.js/pull/506))
- Added Turkish locales to `isAlpha()`, `isAlphanumeric()` and `isMobilePhone()`
- ([#512](https://github.com/chriso/validator.js/pull/512))
+ ([#512](https://github.com/validatorjs/validator.js/pull/512))
- Allow >1 underscore in hostnames when using `allow_underscores`
- ([#510](https://github.com/chriso/validator.js/issues/510))
+ ([#510](https://github.com/validatorjs/validator.js/issues/510))
#### 5.0.0
- Migrate to ES6
- ([#496](https://github.com/chriso/validator.js/pull/496))
+ ([#496](https://github.com/validatorjs/validator.js/pull/496))
- Break the library up so that individual functions can be imported
- ([#496](https://github.com/chriso/validator.js/pull/496))
+ ([#496](https://github.com/validatorjs/validator.js/pull/496))
- Remove auto-coercion of input to a string
- ([#496](https://github.com/chriso/validator.js/pull/496))
+ ([#496](https://github.com/validatorjs/validator.js/pull/496))
- Remove the `extend()` function
- ([#496](https://github.com/chriso/validator.js/pull/496))
+ ([#496](https://github.com/validatorjs/validator.js/pull/496))
- Added Arabic locales to `isAlpha()` and `isAlphanumeric()`
- ([#496](https://github.com/chriso/validator.js/pull/496#issuecomment-184781730))
+ ([#496](https://github.com/validatorjs/validator.js/pull/496#issuecomment-184781730))
- Fix validation of very large base64 strings
- ([#503](https://github.com/chriso/validator.js/pull/503))
+ ([#503](https://github.com/validatorjs/validator.js/pull/503))
#### 4.9.0
- Added a Russian locale to `isAlpha()` and `isAlphanumeric()`
- ([#499](https://github.com/chriso/validator.js/pull/499))
+ ([#499](https://github.com/validatorjs/validator.js/pull/499))
- Remove the restriction on adjacent hyphens in hostnames
- ([#500](https://github.com/chriso/validator.js/issues/500))
+ ([#500](https://github.com/validatorjs/validator.js/issues/500))
#### 4.8.0
- Added Spanish, French, Portuguese and Dutch support for `isAlpha()` and `isAlphanumeric()`
- ([#492](https://github.com/chriso/validator.js/pull/492))
+ ([#492](https://github.com/validatorjs/validator.js/pull/492))
- Added a Brazilian locale to `isMobilePhone()`
- ([#489](https://github.com/chriso/validator.js/pull/489))
+ ([#489](https://github.com/validatorjs/validator.js/pull/489))
- Reject IPv4 addresses with invalid zero padding
- ([#490](https://github.com/chriso/validator.js/pull/490))
+ ([#490](https://github.com/validatorjs/validator.js/pull/490))
- Fix the client-side version when used with RequireJS
- ([#494](https://github.com/chriso/validator.js/issues/494))
+ ([#494](https://github.com/validatorjs/validator.js/issues/494))
#### 4.7.1
- Use [node-depd](https://github.com/dougwilson/nodejs-depd) to print deprecation notices
- ([#487](https://github.com/chriso/validator.js/issues/487))
+ ([#487](https://github.com/validatorjs/validator.js/issues/487))
#### 4.7.0
- Print a deprecation warning if validator input is not a string
- ([1f67e1e](https://github.com/chriso/validator.js/commit/1f67e1e15198c0ae735151290dc8dc2bf14da254)).
+ ([1f67e1e](https://github.com/validatorjs/validator.js/commit/1f67e1e15198c0ae735151290dc8dc2bf14da254)).
Note that this will be an error in v5.
- Added a German locale to `isMobilePhone()`, `isAlpha()` and `isAlphanumeric()`
- ([#477](https://github.com/chriso/validator.js/pull/477))
+ ([#477](https://github.com/validatorjs/validator.js/pull/477))
- Added a Finnish locale to `isMobilePhone()`
- ([#455](https://github.com/chriso/validator.js/pull/455))
+ ([#455](https://github.com/validatorjs/validator.js/pull/455))
#### 4.6.1
- Fix coercion of objects: `Object.toString()` is `[object Object]` not `""`
- ([a57f3c8](https://github.com/chriso/validator.js/commit/a57f3c843c715fba2664ee22ec80e9e28e88e0a6))
+ ([a57f3c8](https://github.com/validatorjs/validator.js/commit/a57f3c843c715fba2664ee22ec80e9e28e88e0a6))
#### 4.6.0
- Added a Spanish locale to `isMobilePhone()`
- ([#481](https://github.com/chriso/validator.js/pull/481))
+ ([#481](https://github.com/validatorjs/validator.js/pull/481))
- Fix string coercion of objects created with `Object.create(null)`
- ([#484](https://github.com/chriso/validator.js/issues/484))
+ ([#484](https://github.com/validatorjs/validator.js/issues/484))
#### 4.5.2
- Fix a timezone issue with short-form ISO 8601 dates, e.g.
`validator.isDate('2011-12-21')`
- ([#480](https://github.com/chriso/validator.js/issues/480))
+ ([#480](https://github.com/validatorjs/validator.js/issues/480))
#### 4.5.1
- Make `isLength()` / `isByteLength()` accept `{min, max}` as options object.
- ([#474](https://github.com/chriso/validator.js/issues/474))
+ ([#474](https://github.com/validatorjs/validator.js/issues/474))
#### 4.5.0
- Add validation for Indian mobile phone numbers
- ([#471](https://github.com/chriso/validator.js/pull/471))
+ ([#471](https://github.com/validatorjs/validator.js/pull/471))
- Tweak Greek and Chinese mobile phone validation
- ([#467](https://github.com/chriso/validator.js/pull/467),
- [#468](https://github.com/chriso/validator.js/pull/468))
+ ([#467](https://github.com/validatorjs/validator.js/pull/467),
+ [#468](https://github.com/validatorjs/validator.js/pull/468))
- Fixed a bug in `isDate()` when validating ISO 8601 dates without a timezone
- ([#472](https://github.com/chriso/validator.js/issues/472))
+ ([#472](https://github.com/validatorjs/validator.js/issues/472))
#### 4.4.1
- Allow triple hyphens in IDNA hostnames
- ([#466](https://github.com/chriso/validator.js/issues/466))
+ ([#466](https://github.com/validatorjs/validator.js/issues/466))
#### 4.4.0
- Added `isMACAddress()` validator
- ([#458](https://github.com/chriso/validator.js/pull/458))
+ ([#458](https://github.com/validatorjs/validator.js/pull/458))
- Added `isWhitelisted()` validator
- ([#462](https://github.com/chriso/validator.js/pull/462))
+ ([#462](https://github.com/validatorjs/validator.js/pull/462))
- Added a New Zealand locale to `isMobilePhone()`
- ([#452](https://github.com/chriso/validator.js/pull/452))
+ ([#452](https://github.com/validatorjs/validator.js/pull/452))
- Added options to control GMail address normalization
- ([#460](https://github.com/chriso/validator.js/pull/460))
+ ([#460](https://github.com/validatorjs/validator.js/pull/460))
#### 4.3.0
- Support Ember CLI module definitions
- ([#448](https://github.com/chriso/validator.js/pull/448))
+ ([#448](https://github.com/validatorjs/validator.js/pull/448))
- Added a Vietnam locale to `isMobilePhone()`
- ([#451](https://github.com/chriso/validator.js/pull/451))
+ ([#451](https://github.com/validatorjs/validator.js/pull/451))
#### 4.2.1
- Fix `isDate()` handling of RFC2822 timezones
- ([#447](https://github.com/chriso/validator.js/pull/447))
+ ([#447](https://github.com/validatorjs/validator.js/pull/447))
#### 4.2.0
- Fix `isDate()` handling of ISO8601 timezones
- ([#444](https://github.com/chriso/validator.js/pull/444))
+ ([#444](https://github.com/validatorjs/validator.js/pull/444))
- Fix the incorrect `isFloat('.') === true`
- ([#443](https://github.com/chriso/validator.js/pull/443))
+ ([#443](https://github.com/validatorjs/validator.js/pull/443))
- Added a Norwegian locale to `isMobilePhone()`
- ([#439](https://github.com/chriso/validator.js/pull/439))
+ ([#439](https://github.com/validatorjs/validator.js/pull/439))
#### 4.1.0
- General `isDate()` improvements
- ([#431](https://github.com/chriso/validator.js/pull/431))
+ ([#431](https://github.com/validatorjs/validator.js/pull/431))
- Tests now require node 4.0+
- ([#438](https://github.com/chriso/validator.js/pull/438))
+ ([#438](https://github.com/validatorjs/validator.js/pull/438))
#### 4.0.6
- Added a Taiwan locale to `isMobilePhone()`
- ([#432](https://github.com/chriso/validator.js/pull/432))
+ ([#432](https://github.com/validatorjs/validator.js/pull/432))
- Fixed a bug in `isBefore()` where it would return `null`
- ([#436](https://github.com/chriso/validator.js/pull/436))
+ ([#436](https://github.com/validatorjs/validator.js/pull/436))
#### 4.0.5
- Fixed a denial of service vulnerability in the `isEmail()` regex
- ([#152](https://github.com/chriso/validator.js/issues/152#issuecomment-131874928))
+ ([#152](https://github.com/validatorjs/validator.js/issues/152#issuecomment-131874928))
#### 4.0.4
- Reverted the leap year validation in `isDate()` as it introduced some regressions
- ([#422](https://github.com/chriso/validator.js/issues/422), [#423](https://github.com/chriso/validator.js/issues/423))
+ ([#422](https://github.com/validatorjs/validator.js/issues/422), [#423](https://github.com/validatorjs/validator.js/issues/423))
#### 4.0.3
- Added leap year validation to `isDate()`
- ([#418](https://github.com/chriso/validator.js/pull/418))
+ ([#418](https://github.com/validatorjs/validator.js/pull/418))
#### 4.0.2
- Fixed `isDecimal()` with an empty string
- ([#419](https://github.com/chriso/validator.js/issues/419))
+ ([#419](https://github.com/validatorjs/validator.js/issues/419))
#### 4.0.1
- Fixed `isByteLength()` with certain strings
- ([09f0c6d](https://github.com/chriso/validator.js/commit/09f0c6d2321f0c78af6a7de42e91b63955e4c01e))
+ ([09f0c6d](https://github.com/validatorjs/validator.js/commit/09f0c6d2321f0c78af6a7de42e91b63955e4c01e))
- Put length restrictions on email parts
- ([#258](https://github.com/chriso/validator.js/issues/258#issuecomment-127173612))
+ ([#258](https://github.com/validatorjs/validator.js/issues/258#issuecomment-127173612))
#### 4.0.0
- Simplified the `isEmail()` regex and fixed some edge cases
- ([#258](https://github.com/chriso/validator.js/issues/258#issuecomment-127173612))
+ ([#258](https://github.com/validatorjs/validator.js/issues/258#issuecomment-127173612))
- Added ISO 8601 date validation via `isISO8601()`
- ([#373](https://github.com/chriso/validator.js/issues/373))
+ ([#373](https://github.com/validatorjs/validator.js/issues/373))
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 000000000..747a30ea2
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,38 @@
+# Contributing to validator.js
+Welcome to validator.js repository!! We appreciate your interest in contributing to this open library and for helping our community grow.
+
+## How to Contribute
+### Code Contribution
+In general, we follow the "fork-and-pull" Git workflow.
+
+1. [Fork](https://docs.github.com/en/get-started/exploring-projects-on-github/contributing-to-a-project) the repository on GitHub
+2. Clone the project to your local machine
+3. Work on your fork
+ * Install the project using `npm install --legacy-peer-deps` (see [issue](https://github.com/validatorjs/validator.js/issues/2123))
+ * Make your changes and additions
+ - Most of your changes should be focused on src/ and test/ folders and/or [README.md](https://github.com/validatorjs/validator.js/blob/master/README.md).
+ - Files such as validator.js, validator.min.js and files in lib/ folder are autogenerated when running tests (npm test) and need not to be changed **manually**.
+ * Change or add tests if needed
+ * Run tests and make sure they pass
+ * Add changes to README.md if needed
+4. Commit changes to your own branch
+5. **Make sure** you merge the latest from "upstream" and resolve conflicts if there is any
+6. Repeat step 3(3) above
+7. Push your work back up to your fork
+8. Submit a Pull request so that we can review your changes
+
+#### Run Tests
+Tests are using mocha. To run the tests use:
+
+```sh
+$ npm test
+```
+
+### Financial Contribution
+We welcome financial contributions on our [open collective](https://opencollective.com/validatorjs).
+
+You can opt to become a [backer](https://opencollective.com/validatorjs#backer) or a [sponsor](https://opencollective.com/validatorjs#sponsor) and help our project sustain over time.
+
+Thank you to the people who have already contributed:
+
+
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
index 37a807cbc..4e49a3840 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2016 Chris O'Hara
+Copyright (c) 2018 Chris O'Hara
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
diff --git a/README.md b/README.md
index 81a8fb5fb..37fbd5e3d 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,12 @@
# validator.js
-
-[![NPM version][npm-image]][npm-url] [![Build Status][travis-image]][travis-url] [![Downloads][downloads-image]][npm-url]
+[![NPM version][npm-image]][npm-url]
+[![CI][ci-image]][ci-url]
+[![Coverage][codecov-image]][codecov-url]
+[![Downloads][downloads-image]][npm-url]
+[](#backers)
+[](#sponsors)
+[](https://github.com/alguerocode/validator.js/blob/master/LICENSE)
+[![Gitter][gitter-image]][gitter-url]
A library of string validators and sanitizers.
@@ -9,13 +15,19 @@ A library of string validators and sanitizers.
**This library validates and sanitizes strings only.**
If you're not sure if your input is a string, coerce it using `input + ''`.
-Passing anything other than a string is an error.
+Passing anything other than a string will result in an error.
## Installation and Usage
### Server-side usage
-Install the library with `npm install validator`
+Install the `validator` package as:
+
+```sh
+npm i validator
+yarn add validator
+pnpm i validator
+```
#### No ES6
@@ -37,6 +49,12 @@ Or, import only a subset of the library:
import isEmail from 'validator/lib/isEmail';
```
+#### Tree-shakeable ES imports
+
+```javascript
+import isEmail from 'validator/es/lib/isEmail';
+```
+
### Client-side usage
The library can be loaded either as a standalone script, or through an [AMD][amd]-compatible loader
@@ -54,64 +72,108 @@ The library can also be installed through [bower][bower]
$ bower install validator-js
```
+CDN
+
+```html
+
+```
+
## Validators
Here is a list of the validators currently available.
Validator | Description
--------------------------------------- | --------------------------------------
-***contains(str, seed)*** | check if the string contains the seed.
+**contains(str, seed [, options])** | check if the string contains the seed.
`options` is an object that defaults to `{ ignoreCase: false, minOccurrences: 1 }`.
Options:
`ignoreCase`: Ignore case when doing comparison, default false.
`minOccurrences`: Minimum number of occurrences for the seed in the string. Defaults to 1.
**equals(str, comparison)** | check if the string matches the comparison.
-**isAfter(str [, date])** | check if the string is a date that's after the specified date (defaults to now).
-**isAlpha(str [, locale])** | check if the string contains only letters (a-zA-Z).
Locale is one of `['ar', 'ar-AE', 'ar-BH', 'ar-DZ', 'ar-EG', 'ar-IQ', 'ar-JO', 'ar-KW', 'ar-LB', 'ar-LY', 'ar-MA', 'ar-QA', 'ar-QM', 'ar-SA', 'ar-SD', 'ar-SY', 'ar-TN', 'ar-YE', 'cs-CZ', 'da-DK', 'de-DE', 'en-AU', 'en-GB', 'en-HK', 'en-IN', 'en-NZ', 'en-US', 'en-ZA', 'en-ZM', 'es-ES', 'fr-FR', 'hu-HU', 'it-IT', 'nb-NO', 'nl-NL', 'nn-NO', 'pl-PL', 'pt-BR', 'pt-PT', 'ru-RU', 'sr-RS', 'sr-RS@latin', 'sv-SE', 'tr-TR', 'uk-UA']`) and defaults to `en-US`.
-**isAlphanumeric(str [, locale])** | check if the string contains only letters and numbers.
Locale is one of `['ar', 'ar-AE', 'ar-BH', 'ar-DZ', 'ar-EG', 'ar-IQ', 'ar-JO', 'ar-KW', 'ar-LB', 'ar-LY', 'ar-MA', 'ar-QA', 'ar-QM', 'ar-SA', 'ar-SD', 'ar-SY', 'ar-TN', 'ar-YE', 'cs-CZ', 'da-DK', 'de-DE', 'en-AU', 'en-GB', 'en-HK', 'en-IN', 'en-NZ', 'en-US', 'en-ZA', 'en-ZM', 'es-ES', 'fr-FR', 'hu-HU', 'it-IT', 'nb-NO', 'nl-NL', 'nn-NO', 'pl-PL', 'pt-BR', 'pt-PT', 'ru-RU', 'sr-RS', 'sr-RS@latin', 'sv-SE', 'tr-TR', 'uk-UA']`) and defaults to `en-US`.
+**isAbaRouting(str)** | check if the string is an ABA routing number for US bank account / cheque.
+**isAfter(str [, options])** | check if the string is a date that is after the specified date.
`options` is an object that defaults to `{ comparisonDate: Date().toString() }`.
**Options:**
`comparisonDate`: Date to compare to. Defaults to `Date().toString()` (now).
+**isAlpha(str [, locale, options])** | check if the string contains only letters (a-zA-Z).
`locale` is one of `['ar', 'ar-AE', 'ar-BH', 'ar-DZ', 'ar-EG', 'ar-IQ', 'ar-JO', 'ar-KW', 'ar-LB', 'ar-LY', 'ar-MA', 'ar-QA', 'ar-QM', 'ar-SA', 'ar-SD', 'ar-SY', 'ar-TN', 'ar-YE', 'bg-BG', 'bn', 'bn-IN', 'cs-CZ', 'da-DK', 'de-DE', 'el-GR', 'en-AU', 'en-GB', 'en-HK', 'en-IN', 'en-NZ', 'en-US', 'en-ZA', 'en-ZM', 'eo', 'es-ES', 'fa-IR', 'fi-FI', 'fr-CA', 'fr-FR', 'gu-IN', 'he', 'hi-IN', 'hu-HU', 'it-IT', 'ja-JP', 'kk-KZ', 'kn-IN', 'ko-KR', 'ku-IQ', 'ml-IN', 'nb-NO', 'nl-NL', 'nn-NO', 'or-IN', 'pa-IN', 'pl-PL', 'pt-BR', 'pt-PT', 'ru-RU', 'si-LK', 'sk-SK', 'sl-SI', 'sr-RS', 'sr-RS@latin', 'sv-SE', 'ta-IN', 'te-IN', 'th-TH', 'tr-TR', 'uk-UA']` and defaults to `en-US`. Locale list is `validator.isAlphaLocales`. `options` is an optional object that can be supplied with the following key(s): `ignore` which can either be a String or RegExp of characters to be ignored e.g. " -" will ignore spaces and -'s.
+**isAlphanumeric(str [, locale, options])** | check if the string contains only letters and numbers (a-zA-Z0-9).
`locale` is one of `['ar', 'ar-AE', 'ar-BH', 'ar-DZ', 'ar-EG', 'ar-IQ', 'ar-JO', 'ar-KW', 'ar-LB', 'ar-LY', 'ar-MA', 'ar-QA', 'ar-QM', 'ar-SA', 'ar-SD', 'ar-SY', 'ar-TN', 'ar-YE', 'bg-BG', 'bn', 'bn-IN', 'cs-CZ', 'da-DK', 'de-DE', 'el-GR', 'en-AU', 'en-GB', 'en-HK', 'en-IN', 'en-NZ', 'en-US', 'en-ZA', 'en-ZM', 'eo', 'es-ES', 'fa-IR', 'fi-FI', 'fr-CA', 'fr-FR', 'gu-IN', 'he', 'hi-IN', 'hu-HU', 'it-IT', 'ja-JP', 'kk-KZ', 'kn-IN', 'ko-KR', 'ku-IQ', 'ml-IN', 'nb-NO', 'nl-NL', 'nn-NO', 'or-IN', 'pa-IN', 'pl-PL', 'pt-BR', 'pt-PT', 'ru-RU', 'si-LK', 'sk-SK', 'sl-SI', 'sr-RS', 'sr-RS@latin', 'sv-SE', 'ta-IN', 'te-IN', 'th-TH', 'tr-TR', 'uk-UA']`) and defaults to `en-US`. Locale list is `validator.isAlphanumericLocales`. `options` is an optional object that can be supplied with the following key(s): `ignore` which can either be a String or RegExp of characters to be ignored e.g. " -" will ignore spaces and -'s.
**isAscii(str)** | check if the string contains ASCII chars only.
-**isBase64(str)** | check if a string is base64 encoded.
-**isBefore(str [, date])** | check if the string is a date that's before the specified date.
-**isBoolean(str)** | check if a string is a boolean.
-**isByteLength(str, options)** | check if the string's length (in UTF-8 bytes) falls in a range.
`options` is an object which defaults to `{min:0, max: undefined}`.
-**isCreditCard(str)** | check if the string is a credit card.
-**isCurrency(str, options)** | check if the string is a valid currency amount.
`options` is an object which defaults to `{symbol: '$', require_symbol: false, allow_space_after_symbol: false, symbol_after_digits: false, allow_negatives: true, parens_for_negatives: false, negative_sign_before_digits: false, negative_sign_after_digits: false, allow_negative_sign_placeholder: false, thousands_separator: ',', decimal_separator: '.', allow_decimal: true, require_decimal: false, digits_after_decimal: [2], allow_space_after_digits: false}`.
**Note:** The array `digits_after_decimal` is filled with the exact number of digits allowd not a range, for example a range 1 to 3 will be given as [1, 2, 3].
-**isDataURI(str)** | check if the string is a [data uri format](https://developer.mozilla.org/en-US/docs/Web/HTTP/data_URIs).
-**isDecimal(str, options)** | check if the string represents a decimal number, such as 0.1, .3, 1.1, 1.00003, 4.0, etc.
`options` is an opject which defaults to `{force_decimal: false, decimal_digits: '1,', locale: 'en-US'}`
`locale` determine the decimal separator and is one of `['ar', 'ar-AE', 'ar-BH', 'ar-DZ', 'ar-EG', 'ar-IQ', 'ar-JO', 'ar-KW', 'ar-LB', 'ar-LY', 'ar-MA', 'ar-QA', 'ar-QM', 'ar-SA', 'ar-SD', 'ar-SY', 'ar-TN', 'ar-YE', 'cs-CZ', 'da-DK', 'de-DE', 'en-AU', 'en-GB', 'en-HK', 'en-IN', 'en-NZ', 'en-US', 'en-ZA', 'en-ZM', 'es-ES', 'fr-FR', 'hu-HU', 'it-IT', 'nb-NO', 'nl-NL', 'nn-NO', 'pl-PL', 'pt-BR', 'pt-PT', 'ru-RU', 'sr-RS', 'sr-RS@latin', 'sv-SE', 'tr-TR', 'uk-UA']`.
**Note:** `decimal_digits` is given as a range like '1,3', a specific value like '3' or min like '1,'.
-**isDivisibleBy(str, number)** | check if the string is a number that's divisible by another.
-**isEmail(str [, options])** | check if the string is an email.
`options` is an object which defaults to `{ allow_display_name: false, require_display_name: false, allow_utf8_local_part: true, require_tld: true }`. If `allow_display_name` is set to true, the validator will also match `Display Name `. If `require_display_name` is set to true, the validator will reject strings without the format `Display Name `. If `allow_utf8_local_part` is set to false, the validator will not allow any non-English UTF8 character in email address' local part. If `require_tld` is set to false, e-mail addresses without having TLD in their domain will also be matched.
-**isEmpty(str)** | check if the string has a length of zero.
-**isFQDN(str [, options])** | check if the string is a fully qualified domain name (e.g. domain.com).
`options` is an object which defaults to `{ require_tld: true, allow_underscores: false, allow_trailing_dot: false }`.
-**isFloat(str [, options])** | check if the string is a float.
`options` is an object which can contain the keys `min`, `max`, `gt`, and/or `lt` to validate the float is within boundaries (e.g. `{ min: 7.22, max: 9.55 }`) it also has `locale` as an option.
`min` and `max` are equivalent to 'greater or equal' and 'less or equal', respectively while `gt` and `lt` are their strict counterparts.
`locale` determine the decimal separator and is one of `['ar', 'ar-AE', 'ar-BH', 'ar-DZ', 'ar-EG', 'ar-IQ', 'ar-JO', 'ar-KW', 'ar-LB', 'ar-LY', 'ar-MA', 'ar-QA', 'ar-QM', 'ar-SA', 'ar-SD', 'ar-SY', 'ar-TN', 'ar-YE', 'cs-CZ', 'da-DK', 'de-DE', 'en-AU', 'en-GB', 'en-HK', 'en-IN', 'en-NZ', 'en-US', 'en-ZA', 'en-ZM', 'es-ES', 'fr-FR', 'hu-HU', 'it-IT', 'nb-NO', 'nl-NL', 'nn-NO', 'pl-PL', 'pt-BR', 'pt-PT', 'ru-RU', 'sr-RS', 'sr-RS@latin', 'sv-SE', 'tr-TR', 'uk-UA']`.
+**isBase32(str [, options])** | check if the string is base32 encoded. `options` is optional and defaults to `{ crockford: false }`.
When `crockford` is true it tests the given base32 encoded string using [Crockford's base32 alternative][Crockford Base32].
+**isBase58(str)** | check if the string is base58 encoded.
+**isBase64(str [, options])** | check if the string is base64 encoded. `options` is optional and defaults to `{ urlSafe: false, padding: true }`
when `urlSafe` is true default value for `padding` is false and it tests the given base64 encoded string is [url safe][Base64 URL Safe].
+**isBefore(str [, options])** | check if the string is a date that is before the specified date.
`options` is an object that defaults to `{ comparisonDate: Date().toString() }`.
**Options:**
`comparisonDate`: Date to compare to. Defaults to `Date().toString()` (now).
+**isBIC(str)** | check if the string is a BIC (Bank Identification Code) or SWIFT code.
+**isBoolean(str [, options])** | check if the string is a boolean.
`options` is an object which defaults to `{ loose: false }`. If `loose` is set to false, the validator will strictly match ['true', 'false', '0', '1']. If `loose` is set to true, the validator will also match 'yes', 'no', and will match a valid boolean string of any case. (e.g.: ['true', 'True', 'TRUE']).
+**isBtcAddress(str)** | check if the string is a valid BTC address.
+**isByteLength(str [, options])** | check if the string's length (in UTF-8 bytes) falls in a range.
`options` is an object which defaults to `{ min: 0, max: undefined }`.
+**isCreditCard(str [, options])** | check if the string is a credit card number.
`options` is an optional object that can be supplied with the following key(s): `provider` is an optional key whose value should be a string, and defines the company issuing the credit card. Valid values include `['amex', 'dinersclub', 'discover', 'jcb', 'mastercard', 'unionpay', 'visa']` or blank will check for any provider.
+**isCurrency(str [, options])** | check if the string is a valid currency amount.
`options` is an object which defaults to `{ symbol: '$', require_symbol: false, allow_space_after_symbol: false, symbol_after_digits: false, allow_negatives: true, parens_for_negatives: false, negative_sign_before_digits: false, negative_sign_after_digits: false, allow_negative_sign_placeholder: false, thousands_separator: ',', decimal_separator: '.', allow_decimal: true, require_decimal: false, digits_after_decimal: [2], allow_space_after_digits: false }`.
**Note:** The array `digits_after_decimal` is filled with the exact number of digits allowed not a range, for example a range 1 to 3 will be given as [1, 2, 3].
+**isDataURI(str)** | check if the string is a [data uri format][Data URI Format].
+**isDate(str [, options])** | check if the string is a valid date. e.g. [`2002-07-15`, new Date()].
`options` is an object which can contain the keys `format`, `strictMode` and/or `delimiters`.
`format` is a string and defaults to `YYYY/MM/DD`.
`strictMode` is a boolean and defaults to `false`. If `strictMode` is set to true, the validator will reject strings different from `format`.
`delimiters` is an array of allowed date delimiters and defaults to `['/', '-']`.
+**isDecimal(str [, options])** | check if the string represents a decimal number, such as 0.1, .3, 1.1, 1.00003, 4.0, etc.
`options` is an object which defaults to `{force_decimal: false, decimal_digits: '1,', locale: 'en-US'}`.
`locale` determines the decimal separator and is one of `['ar', 'ar-AE', 'ar-BH', 'ar-DZ', 'ar-EG', 'ar-IQ', 'ar-JO', 'ar-KW', 'ar-LB', 'ar-LY', 'ar-MA', 'ar-QA', 'ar-QM', 'ar-SA', 'ar-SD', 'ar-SY', 'ar-TN', 'ar-YE', 'bg-BG', 'cs-CZ', 'da-DK', 'de-DE', 'el-GR', 'en-AU', 'en-GB', 'en-HK', 'en-IN', 'en-NZ', 'en-US', 'en-ZA', 'en-ZM', 'eo', 'es-ES', 'fa', 'fa-AF', 'fa-IR', 'fr-FR', 'fr-CA', 'hu-HU', 'id-ID', 'it-IT', 'ku-IQ', 'nb-NO', 'nl-NL', 'nn-NO', 'pl-PL', 'pl-Pl', 'pt-BR', 'pt-PT', 'ru-RU', 'sl-SI', 'sr-RS', 'sr-RS@latin', 'sv-SE', 'tr-TR', 'uk-UA', 'vi-VN']`.
**Note:** `decimal_digits` is given as a range like '1,3', a specific value like '3' or min like '1,'.
+**isDivisibleBy(str, number)** | check if the string is a number that is divisible by another.
+**isEAN(str)** | check if the string is an [EAN (European Article Number)][European Article Number].
+**isEmail(str [, options])** | check if the string is an email.
`options` is an object which defaults to `{ allow_display_name: false, require_display_name: false, allow_utf8_local_part: true, require_tld: true, allow_ip_domain: false, allow_underscores: false, domain_specific_validation: false, blacklisted_chars: '', host_blacklist: [] }`. If `allow_display_name` is set to true, the validator will also match `Display Name `. If `require_display_name` is set to true, the validator will reject strings without the format `Display Name `. If `allow_utf8_local_part` is set to false, the validator will not allow any non-English UTF8 character in email address' local part. If `require_tld` is set to false, email addresses without a TLD in their domain will also be matched. If `ignore_max_length` is set to true, the validator will not check for the standard max length of an email. If `allow_ip_domain` is set to true, the validator will allow IP addresses in the host part. If `domain_specific_validation` is true, some additional validation will be enabled, e.g. disallowing certain syntactically valid email addresses that are rejected by Gmail. If `blacklisted_chars` receives a string, then the validator will reject emails that include any of the characters in the string, in the name part. If `host_blacklist` is set to an array of strings or regexp, and the part of the email after the `@` symbol matches one of the strings defined in it, the validation fails. If `host_whitelist` is set to an array of strings or regexp, and the part of the email after the `@` symbol matches none of the strings defined in it, the validation fails.
+**isEmpty(str [, options])** | check if the string has a length of zero.
`options` is an object which defaults to `{ ignore_whitespace: false }`.
+**isEthereumAddress(str)** | check if the string is an [Ethereum][Ethereum] address. Does not validate address checksums.
+**isFloat(str [, options])** | check if the string is a float.
`options` is an object which can contain the keys `min`, `max`, `gt`, and/or `lt` to validate the float is within boundaries (e.g. `{ min: 7.22, max: 9.55 }`) it also has `locale` as an option.
`min` and `max` are equivalent to 'greater or equal' and 'less or equal', respectively while `gt` and `lt` are their strict counterparts.
`locale` determines the decimal separator and is one of `['ar', 'ar-AE', 'ar-BH', 'ar-DZ', 'ar-EG', 'ar-IQ', 'ar-JO', 'ar-KW', 'ar-LB', 'ar-LY', 'ar-MA', 'ar-QA', 'ar-QM', 'ar-SA', 'ar-SD', 'ar-SY', 'ar-TN', 'ar-YE', 'bg-BG', 'cs-CZ', 'da-DK', 'de-DE', 'en-AU', 'en-GB', 'en-HK', 'en-IN', 'en-NZ', 'en-US', 'en-ZA', 'en-ZM', 'eo', 'es-ES', 'fr-CA', 'fr-FR', 'hu-HU', 'it-IT', 'nb-NO', 'nl-NL', 'nn-NO', 'pl-PL', 'pt-BR', 'pt-PT', 'ru-RU', 'sl-SI', 'sr-RS', 'sr-RS@latin', 'sv-SE', 'tr-TR', 'uk-UA']`. Locale list is `validator.isFloatLocales`.
+**isFQDN(str [, options])** | check if the string is a fully qualified domain name (e.g. domain.com).
`options` is an object which defaults to `{ require_tld: true, allow_underscores: false, allow_trailing_dot: false, allow_numeric_tld: false, allow_wildcard: false, ignore_max_length: false }`.
`require_tld` - If set to false the validator will not check if the domain includes a TLD.
`allow_underscores` - if set to true, the validator will allow underscores in the domain.
`allow_trailing_dot` - if set to true, the validator will allow the domain to end with a `.` character.
`allow_numeric_tld` - if set to true, the validator will allow the TLD of the domain to be made up solely of numbers.
`allow_wildcard` - if set to true, the validator will allow domains starting with `*.` (e.g. `*.example.com` or `*.shop.example.com`).
`ignore_max_length` - if set to true, the validator will not check for the standard max length of a domain.
+**isFreightContainerID(str)** | alias for `isISO6346`, check if the string is a valid [ISO 6346](https://en.wikipedia.org/wiki/ISO_6346) shipping container identification.
**isFullWidth(str)** | check if the string contains any full-width chars.
**isHalfWidth(str)** | check if the string contains any half-width chars.
-**isHash(str, 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']`
-**isHexColor(str)** | check if the string is a hexadecimal color.
+**isHash(str, algorithm)** | check if the string is a hash of type algorithm.
Algorithm is one of `['crc32', 'crc32b', 'md4', 'md5', 'ripemd128', 'ripemd160', 'sha1', 'sha256', 'sha384', 'sha512', 'tiger128', 'tiger160', 'tiger192']`.
**isHexadecimal(str)** | check if the string is a hexadecimal number.
-**isIP(str [, version])** | check if the string is an IP (version 4 or 6).
-**isISBN(str [, version])** | check if the string is an ISBN (version 10 or 13).
-**isISSN(str [, options])** | check if the string is an [ISSN](https://en.wikipedia.org/wiki/International_Standard_Serial_Number).
`options` is an object which defaults to `{ case_sensitive: false, require_hyphen: false }`. If `case_sensitive` is true, ISSNs with a lowercase `'x'` as the check digit are rejected.
-**isISIN(str)** | check if the string is an [ISIN][ISIN] (stock/security identifier).
-**isISO8601(str)** | check if the string is a valid [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date.
-**isISO31661Alpha2(str)** | 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.
-**isISRC(str)** | check if the string is a [ISRC](https://en.wikipedia.org/wiki/International_Standard_Recording_Code).
-**isIn(str, values)** | check if the string is in a array of allowed values.
+**isHexColor(str)** | check if the string is a hexadecimal color.
+**isHSL(str)** | check if the string is an HSL (hue, saturation, lightness, optional alpha) color based on [CSS Colors Level 4 specification][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)`).
+**isIBAN(str, [, options])** | check if the string is an IBAN (International Bank Account Number).
`options` is an object which accepts two attributes: `whitelist`: where you can restrict IBAN codes you want to receive data from and `blacklist`: where you can remove some of the countries from the current list. For both you can use an array with the following values `['AD','AE','AL','AT','AZ','BA','BE','BG','BH','BR','BY','CH','CR','CY','CZ','DE','DK','DO','EE','EG','ES','FI','FO','FR','GB','GE','GI','GL','GR','GT','HR','HU','IE','IL','IQ','IR','IS','IT','JO','KW','KZ','LB','LC','LI','LT','LU','LV','MC','MD','ME','MK','MR','MT','MU','MZ','NL','NO','PK','PL','PS','PT','QA','RO','RS','SA','SC','SE','SI','SK','SM','SV','TL','TN','TR','UA','VA','VG','XK']`.
+**isIdentityCard(str [, locale])** | check if the string is a valid identity card code.
`locale` is one of `['LK', 'PL', 'ES', 'FI', 'IN', 'IT', 'IR', 'MZ', 'NO', 'TH', 'zh-TW', 'he-IL', 'ar-LY', 'ar-TN', 'zh-CN', 'zh-HK', 'PK']` OR `'any'`. If 'any' is used, function will check if any of the locales match.
Defaults to 'any'.
+**isIMEI(str [, options]))** | check if the string is a valid [IMEI number][IMEI]. IMEI should be of format `###############` or `##-######-######-#`.
`options` is an object which can contain the keys `allow_hyphens`. Defaults to first format. If `allow_hyphens` is set to true, the validator will validate the second format.
+**isIn(str, values)** | check if the string is in an array of allowed values.
**isInt(str [, options])** | check if the string is an integer.
`options` is an object which can contain the keys `min` and/or `max` to check the integer is within boundaries (e.g. `{ min: 10, max: 99 }`). `options` can also contain the key `allow_leading_zeroes`, which when set to false will disallow integer values with leading zeroes (e.g. `{ allow_leading_zeroes: false }`). Finally, `options` can contain the keys `gt` and/or `lt` which will enforce integers being greater than or less than, respectively, the value provided (e.g. `{gt: 1, lt: 4}` for a number between 1 and 4).
-**isJSON(str)** | check if the string is valid JSON (note: uses JSON.parse).
-**isLatLong(str)** | check if the string is a valid latitude-longitude coordinate in the format `lat,long` or `lat, long`.
-**isLength(str, options)** | check if the string's length falls in a range.
`options` is an object which defaults to `{min:0, max: undefined}`. Note: this function takes into account surrogate pairs.
+**isIP(str [, options])** | check if the string is an IP address (version 4 or 6).
`options` is an object that defaults to `{ version: '' }`.
**Options:**
`version`: defines which IP version to compare to. Accepted values: `4`, `6`, `'4'`, `'6'`.
+**isIPRange(str [, version])** | check if the string is an IP Range (version 4 or 6).
+**isISBN(str [, options])** | check if the string is an [ISBN][ISBN].
`options` is an object that has no default.
**Options:**
`version`: ISBN version to compare to. Accepted values are '10' and '13'. If none provided, both will be tested.
+**isISIN(str)** | check if the string is an [ISIN][ISIN] (stock/security identifier).
+**isISO6346(str)** | check if the string is a valid [ISO 6346](https://en.wikipedia.org/wiki/ISO_6346) shipping container identification.
+**isISO6391(str)** | check if the string is a valid [ISO 639-1][ISO 639-1] language code.
+**isISO8601(str [, options])** | check if the string is a valid [ISO 8601][ISO 8601] date.
`options` is an object which defaults to `{ strict: false, strictSeparator: false }`. If `strict` is true, date strings with invalid dates like `2009-02-29` will be invalid. If `strictSeparator` is true, date strings with date and time separated by anything other than a T will be invalid.
+**isISO15924(str)** | check if the string is a valid [ISO 15924][ISO 15924] officially assigned script code.
+**isISO31661Alpha2(str)** | check if the string is a valid [ISO 3166-1 alpha-2][ISO 3166-1 alpha-2] officially assigned country code.
+**isISO31661Alpha3(str)** | check if the string is a valid [ISO 3166-1 alpha-3][ISO 3166-1 alpha-3] officially assigned country code.
+**isISO31661Numeric(str)** | check if the string is a valid [ISO 3166-1 numeric][ISO 3166-1 numeric] officially assigned country code.
+**isISO4217(str)** | check if the string is a valid [ISO 4217][ISO 4217] officially assigned currency code.
+**isISRC(str)** | check if the string is an [ISRC][ISRC].
+**isISSN(str [, options])** | check if the string is an [ISSN][ISSN].
`options` is an object which defaults to `{ case_sensitive: false, require_hyphen: false }`. If `case_sensitive` is true, ISSNs with a lowercase `'x'` as the check digit are rejected.
+**isJSON(str [, options])** | check if the string is valid JSON (note: uses JSON.parse).
`options` is an object which defaults to `{ allow_primitives: false }`. If `allow_primitives` is true, the primitives 'true', 'false' and 'null' are accepted as valid JSON values.
+**isJWT(str)** | check if the string is valid JWT token.
+**isLatLong(str [, options])** | check if the string is a valid latitude-longitude coordinate in the format `lat,long` or `lat, long`.
`options` is an object that defaults to `{ checkDMS: false }`. Pass `checkDMS` as `true` to validate DMS(degrees, minutes, and seconds) latitude-longitude format.
+**isLength(str [, options])** | check if the string's length falls in a range and equal to any of the integers of the `discreteLengths` array if provided.
`options` is an object which defaults to `{ min: 0, max: undefined, discreteLengths: undefined }`. Note: this function takes into account surrogate pairs.
+**isLicensePlate(str, locale)** | check if the string matches the format of a country's license plate.
`locale` is one of `['cs-CZ', 'de-DE', 'de-LI', 'en-IN', 'en-SG', 'en-PK', 'es-AR', 'hu-HU', 'pt-BR', 'pt-PT', 'sq-AL', 'sv-SE']` or `'any'`.
+**isLocale(str)** | check if the string is a locale.
**isLowercase(str)** | check if the string is lowercase.
-**isMACAddress(str)** | check if the string is a MAC address.
-**isMD5(str)** | check if the string is a MD5 hash.
-**isMobilePhone(str, locale)** | check if the string is a mobile phone number,
(locale is one of `['ar-AE', 'ar-DZ','ar-EG', 'ar-JO', 'ar-SA', 'ar-SY', 'cs-CZ', 'de-DE', 'da-DK', 'el-GR', 'en-AU', 'en-CA', 'en-GB', 'en-HK', 'en-IN', 'en-KE', 'en-NG', 'en-NZ', 'en-RW', 'en-SG', 'en-UG', 'en-US', 'en-TZ', 'en-ZA', 'en-ZM', 'en-PK', 'es-ES', 'et-EE', 'fa-IR', 'fi-FI', 'fr-FR', 'he-IL', 'hu-HU', 'it-IT', 'ja-JP', 'ko-KR', 'lt-LT', 'ms-MY', 'nb-NO', 'nn-NO', 'pl-PL', 'pt-PT', 'ro-RO', 'ru-RU', 'sk-SK', 'sr-RS', 'tr-TR', 'uk-UA', 'vi-VN', 'zh-CN', 'zh-HK', 'zh-TW']` OR 'any'. If 'any' is used, function will check if any of the locales match).
+**isLuhnNumber(str)** | check if the string passes the [Luhn algorithm check](https://en.wikipedia.org/wiki/Luhn_algorithm).
+**isMACAddress(str [, options])** | check if the string is a MAC address.
`options` is an object which defaults to `{ no_separators: false }`. It allows the use of hyphens, spaces or dots e.g. '01 02 03 04 05 ab', '01-02-03-04-05-ab' or '0102.0304.05ab'. If `no_separators` is true, the validator will then only check MAC addresses without separators. The options also allow a `eui` property to specify if it needs to be validated against EUI-48 or EUI-64. The accepted values of `eui` are: 48, 64.
+**isMagnetURI(str)** | check if the string is a [Magnet URI format][Magnet URI Format].
+**isMailtoURI(str, [, options])** | check if the string is a [Mailto URI format][Mailto URI Format].
`options` is an object of validating emails inside the URI (check `isEmail`s options for details).
+**isMD5(str)** | check if the string is a MD5 hash.
Please note that you can also use the `isHash(str, 'md5')` function. Keep in mind that MD5 has some collision weaknesses compared to other algorithms (e.g., SHA).
+**isMimeType(str)** | check if the string matches to a valid [MIME type][MIME Type] format.
+**isMobilePhone(str [, locale [, options]])** | check 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-EH', 'ar-IQ', 'ar-JO', 'ar-KW', 'ar-PS', 'ar-SA', 'ar-SD', 'ar-SY', 'ar-TN', 'ar-YE', 'az-AZ', 'az-LB', 'az-LY', 'be-BY', 'bg-BG', 'bn-BD', 'bs-BA', 'ca-AD', 'cs-CZ', 'da-DK', 'de-AT', 'de-CH', 'de-DE', 'de-LU', 'dv-MV', 'dz-BT', 'el-CY', 'el-GR', 'en-AG', 'en-AI', 'en-AU', 'en-BM', 'en-BS', 'en-BW', 'en-CA', 'en-GB', 'en-GG', 'en-GH', 'en-GY', 'en-HK', 'en-IE', 'en-IN', 'en-JM', 'en-KE', 'en-KI', 'en-KN', 'en-LS', 'en-MO', 'en-MT', 'en-MU', 'en-MW', 'en-NG', 'en-NZ', 'en-PG', 'en-PH', 'en-PK', 'en-RW', 'en-SG', 'en-SL', 'en-SS', 'en-TZ', 'en-UG', 'en-US', 'en-ZA', 'en-ZM', 'en-ZW', 'es-AR', 'es-BO', 'es-CL', 'es-CO', 'es-CR', 'es-CU', 'es-DO', 'es-EC', 'es-ES', 'es-GT','es-HN', 'es-MX', 'es-NI', 'es-PA', 'es-PE', 'es-PY', 'es-SV', 'es-UY', 'es-VE', 'et-EE', 'fa-AF', 'fa-IR', 'fi-FI', 'fj-FJ', 'fo-FO', 'fr-BE', 'fr-BF', 'fr-BJ', 'fr-CD', 'fr-CF', 'fr-FR', 'fr-GF', 'fr-GP', 'fr-MQ', 'fr-PF', 'fr-RE', 'fr-WF', 'ga-IE', 'he-IL', 'hu-HU', 'id-ID', 'ir-IR', 'it-IT', 'it-SM', 'ja-JP', 'ka-GE', 'kk-KZ', 'kl-GL', 'ko-KR', 'ky-KG', 'lt-LT', 'mg-MG', 'mn-MN', 'mk-MK', 'ms-MY', 'my-MM', 'mz-MZ', 'nb-NO', 'ne-NP', 'nl-AW', 'nl-BE', 'nl-NL', 'nn-NO', 'pl-PL', 'pt-AO', 'pt-BR', 'pt-PT', 'ro-Md', 'ro-RO', 'ru-RU', 'si-LK', 'sk-SK', 'sl-SI', 'so-SO', 'sq-AL', 'sr-RS', 'sv-SE', 'tg-TJ', 'th-TH', 'tk-TM', 'tr-TR', 'uk-UA', 'uz-UZ', 'vi-VN', 'zh-CN', 'zh-HK', 'zh-MO', 'zh-TW']` OR defaults to `'any'`. If 'any' or a falsey value is used, function will check if any of the locales match).
`options` is an optional object that can be supplied with the following keys: `strictMode`, if this is set to `true`, the mobile phone number must be supplied with the country code and therefore must start with `+`. Locale list is `validator.isMobilePhoneLocales`.
**isMongoId(str)** | check if the string is a valid hex-encoded representation of a [MongoDB ObjectId][mongoid].
**isMultibyte(str)** | check if the string contains one or more multibyte chars.
-**isNumeric(str)** | check if the string contains only numbers.
+**isNumeric(str [, options])** | check if the string contains only numbers.
`options` is an object which defaults to `{ no_symbols: false }` it also has `locale` as an option. If `no_symbols` is true, the validator will reject numeric strings that feature a symbol (e.g. `+`, `-`, or `.`).
`locale` determines the decimal separator and is one of `['ar', 'ar-AE', 'ar-BH', 'ar-DZ', 'ar-EG', 'ar-IQ', 'ar-JO', 'ar-KW', 'ar-LB', 'ar-LY', 'ar-MA', 'ar-QA', 'ar-QM', 'ar-SA', 'ar-SD', 'ar-SY', 'ar-TN', 'ar-YE', 'bg-BG', 'cs-CZ', 'da-DK', 'de-DE', 'en-AU', 'en-GB', 'en-HK', 'en-IN', 'en-NZ', 'en-US', 'en-ZA', 'en-ZM', 'eo', 'es-ES', 'fr-FR', 'fr-CA', 'hu-HU', 'it-IT', 'nb-NO', 'nl-NL', 'nn-NO', 'pl-PL', 'pt-BR', 'pt-PT', 'ru-RU', 'sl-SI', 'sr-RS', 'sr-RS@latin', 'sv-SE', 'tr-TR', 'uk-UA']`.
+**isOctal(str)** | check if the string is a valid octal number.
+**isPassportNumber(str, countryCode)** | check if the string is a valid passport number.
`countryCode` is one of `['AM', 'AR', 'AT', 'AU', 'AZ', 'BE', 'BG', 'BY', 'BR', 'CA', 'CH', 'CN', 'CY', 'CZ', 'DE', 'DK', 'DZ', 'EE', 'ES', 'FI', 'FR', 'GB', 'GR', 'HR', 'HU', 'IE', 'IN', 'IR', 'ID', 'IS', 'IT', 'JM', 'JP', 'KR', 'KZ', 'LI', 'LT', 'LU', 'LV', 'LY', 'MT', 'MX', 'MY', 'MZ', 'NL', 'NZ', 'PH', 'PK', 'PL', 'PT', 'RO', 'RU', 'SE', 'SL', 'SK', 'TH', 'TR', 'UA', 'US', 'ZA']`. Locale list is `validator.passportNumberLocales`.
**isPort(str)** | check if the string is a valid port number.
-**isPostalCode(str, locale)** | check if the string is a postal code,
(locale is one of `[ 'AT', 'AU', 'BE', 'CA', 'CH', 'CZ', 'DE', 'DK', 'DZ', 'ES', 'FI', 'FR', 'GB', 'GR', 'IL', 'IN', 'IS', 'IT', 'JP', 'KE', 'LI', 'MX', 'NL', 'NO', 'PL', 'PT', 'RO', 'RU', 'SA', 'SE', 'TW', 'US', 'ZA', 'ZM' ]` OR 'any'. If 'any' is used, function will check if any of the locals match).
+**isPostalCode(str, locale)** | check if the string is a postal code.
`locale` is one of `['AD', 'AT', 'AU', 'AZ', 'BA', 'BD', 'BE', 'BG', 'BR', 'BY', 'CA', 'CH', 'CN', 'CO', 'CZ', 'DE', 'DK', 'DO', 'DZ', 'EE', 'ES', 'FI', 'FR', 'GB', 'GR', 'HR', 'HT', 'HU', 'ID', 'IE', 'IL', 'IN', 'IR', 'IS', 'IT', 'JP', 'KE', 'KR', 'LI', 'LK', 'LT', 'LU', 'LV', 'MG', 'MT', 'MX', 'MY', 'NL', 'NO', 'NP', 'NZ', 'PK', 'PL', 'PR', 'PT', 'RO', 'RU', 'SA', 'SE', 'SG', 'SI', 'SK', 'TH', 'TN', 'TW', 'UA', 'US', 'ZA', 'ZM']` OR `'any'`. If 'any' is used, function will check if any of the locales match. Locale list is `validator.isPostalCodeLocales`.
+**isRFC3339(str)** | check if the string is a valid [RFC 3339][RFC 3339] date.
+**isRgbColor(str [,options])** | check if the string is a rgb or rgba color.
`options` is an object with the following properties
`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.
`allowSpaces` defaults to `true`, which prohibits whitespace. If set to false, whitespace between color values is allowed, such as `rgb(255, 255, 255)` or even `rgba(255, 128, 0, 0.7)`.
+**isSemVer(str)** | check if the string is a Semantic Versioning Specification (SemVer).
**isSurrogatePair(str)** | check if the string contains any surrogate pairs chars.
-**isURL(str [, options])** | check if the string is an URL.
`options` is an object which defaults to `{ protocols: ['http','https','ftp'], require_tld: true, require_protocol: false, require_host: true, require_valid_protocol: true, allow_underscores: false, host_whitelist: false, host_blacklist: false, allow_trailing_dot: false, allow_protocol_relative_urls: false }`.
-**isUUID(str [, version])** | check if the string is a UUID (version 3, 4 or 5).
**isUppercase(str)** | check if the string is uppercase.
+**isSlug(str)** | check if the string is of type slug.
+**isStrongPassword(str [, options])** | check if the string can be considered a strong password or not. Allows for custom requirements or scoring rules. If `returnScore` is true, then the function returns an integer score for the password rather than a boolean.
Default options:
`{ minLength: 8, minLowercase: 1, minUppercase: 1, minNumbers: 1, minSymbols: 1, returnScore: false, pointsPerUnique: 1, pointsPerRepeat: 0.5, pointsForContainingLower: 10, pointsForContainingUpper: 10, pointsForContainingNumber: 10, pointsForContainingSymbol: 10 }`
+**isTime(str [, options])** | check if the string is a valid time e.g. [`23:01:59`, new Date().toLocaleTimeString()].
`options` is an object which can contain the keys `hourFormat` or `mode`.
`hourFormat` is a key and defaults to `'hour24'`.
`mode` is a key and defaults to `'default'`.
`hourFormat` can contain the values `'hour12'` or `'hour24'`, `'hour24'` will validate hours in 24 format and `'hour12'` will validate hours in 12 format.
`mode` can contain the values `'default', 'withSeconds', withOptionalSeconds`, `'default'` will validate `HH:MM` format, `'withSeconds'` will validate the `HH:MM:SS` format, `'withOptionalSeconds'` will validate `'HH:MM'` and `'HH:MM:SS'` formats.
+**isTaxID(str, locale)** | check if the string is a valid Tax Identification Number. Default locale is `en-US`.
More info about exact TIN support can be found in `src/lib/isTaxID.js`.
Supported locales: `[ 'bg-BG', 'cs-CZ', 'de-AT', 'de-DE', 'dk-DK', 'el-CY', 'el-GR', 'en-CA', 'en-GB', 'en-IE', 'en-US', 'es-AR', 'es-ES', 'et-EE', 'fi-FI', 'fr-BE', 'fr-CA', 'fr-FR', 'fr-LU', 'hr-HR', 'hu-HU', 'it-IT', 'lb-LU', 'lt-LT', 'lv-LV', 'mt-MT', 'nl-BE', 'nl-NL', 'pl-PL', 'pt-BR', 'pt-PT', 'ro-RO', 'sk-SK', 'sl-SI', 'sv-SE', 'uk-UA']`.
+**isURL(str [, options])** | check if the string is a URL.
`options` is an object which defaults to `{ protocols: ['http','https','ftp'], require_tld: true, require_protocol: false, require_host: true, require_port: false, require_valid_protocol: true, allow_underscores: false, host_whitelist: false, host_blacklist: false, allow_trailing_dot: false, allow_protocol_relative_urls: false, allow_fragments: true, allow_query_components: true, disallow_auth: false, validate_length: true }`.
`protocols` - valid protocols can be modified with this option.
`require_tld` - If set to false isURL will not check if the URL's host includes a top-level domain.
`require_protocol` - **RECOMMENDED** if set to true isURL will return false if protocol is not present in the URL. Without this setting, some malicious URLs cannot be distinguishable from a valid URL with authentication information.
`require_host` - if set to false isURL will not check if host is present in the URL.
`require_port` - if set to true isURL will check if port is present in the URL.
`require_valid_protocol` - isURL will check if the URL's protocol is present in the protocols option.
`allow_underscores` - if set to true, the validator will allow underscores in the URL.
`host_whitelist` - if set to an array of strings or regexp, and the domain matches none of the strings defined in it, the validation fails.
`host_blacklist` - if set to an array of strings or regexp, and the domain matches any of the strings defined in it, the validation fails.
`allow_trailing_dot` - if set to true, the validator will allow the domain to end with a `.` character.
`allow_protocol_relative_urls` - if set to true protocol relative URLs will be allowed.
`allow_fragments` - if set to false isURL will return false if fragments are present.
`allow_query_components` - if set to false isURL will return false if query components are present.
`disallow_auth` - if set to true, the validator will fail if the URL contains an authentication component, e.g. `http://username:password@example.com`.
`validate_length` - if set to false isURL will skip string length validation. `max_allowed_length` will be ignored if this is set as `false`.
`max_allowed_length` - if set, isURL will not allow URLs longer than the specified value (default is 2084 that IE maximum URL length).
+**isULID(str)** | check if the string is a [ULID](https://github.com/ulid/spec).
+**isUUID(str [, version])** | check if the string is an RFC9562 UUID.
`version` is one of `'1'`-`'8'`, `'nil'`, `'max'`, `'all'` or `'loose'`. The `'loose'` option checks if the string is a UUID-like string with hexadecimal values, ignoring RFC9565.
**isVariableWidth(str)** | check if the string contains a mixture of full and half-width chars.
-**isWhitelisted(str, chars)** | checks characters if they appear in the whitelist.
-**matches(str, pattern [, modifiers])** | check if string matches the pattern.
Either `matches('foo', /foo/i)` or `matches('foo', 'foo', 'i')`.
+**isVAT(str, countryCode)** | check if the string is a [valid VAT number][VAT Number] if validation is available for the given country code matching [ISO 3166-1 alpha-2][ISO 3166-1 alpha-2].
`countryCode` is one of `['AL', 'AR', 'AT', 'AU', 'BE', 'BG', 'BO', 'BR', 'BY', 'CA', 'CH', 'CL', 'CO', 'CR', 'CY', 'CZ', 'DE', 'DK', 'DO', 'EC', 'EE', 'EL', 'ES', 'FI', 'FR', 'GB', 'GT', 'HN', 'HR', 'HU', 'ID', 'IE', 'IL', 'IN', 'IS', 'IT', 'KZ', 'LT', 'LU', 'LV', 'MK', 'MT', 'MX', 'NG', 'NI', 'NL', 'NO', 'NZ', 'PA', 'PE', 'PH', 'PL', 'PT', 'PY', 'RO', 'RS', 'RU', 'SA', 'SE', 'SI', 'SK', 'SM', 'SV', 'TR', 'UA', 'UY', 'UZ', 'VE']`.
+**isWhitelisted(str, chars)** | check if the string consists only of characters that appear in the whitelist `chars`.
+**matches(str, pattern [, modifiers])** | check if the string matches the pattern.
Either `matches('foo', /foo/i)` or `matches('foo', 'foo', 'i')`.
## Sanitizers
@@ -120,10 +182,9 @@ Here is a list of the sanitizers currently available.
Sanitizer | Description
-------------------------------------- | -------------------------------
**blacklist(input, chars)** | remove characters that appear in the blacklist. The characters are used in a RegExp and so you will need to escape some chars, e.g. `blacklist(input, '\\[\\]')`.
-**escape(input)** | replace `<`, `>`, `&`, `'`, `"` and `/` with HTML entities.
-**unescape(input)** | replaces HTML encoded entities with `<`, `>`, `&`, `'`, `"` and `/`.
+**escape(input)** | replace `<`, `>`, `&`, `'`, `"`, `` ` ``, `\` and `/` with HTML entities.
**ltrim(input [, chars])** | trim characters from the left-side of the input.
-**normalizeEmail(email [, options])** | canonicalizes an email address. (This doesn't validate that the input is an email, if you want to validate the email use isEmail beforehand)
`options` is an object with the following keys and default values:
- *all_lowercase: true* - Transforms the local part (before the @ symbol) of all email addresses to lowercase. Please note that this may violate RFC 5321, which gives providers the possibility to treat the local part of email addresses in a case sensitive way (although in practice most - yet not all - providers don't). The domain part of the email address is always lowercased, as it's case insensitive per RFC 1035.
- *gmail_lowercase: true* - GMail addresses are known to be case-insensitive, so this switch allows lowercasing them even when *all_lowercase* is set to false. Please note that when *all_lowercase* is true, GMail addresses are lowercased regardless of the value of this setting.
- *gmail_remove_dots: true*: Removes dots from the local part of the email address, as GMail ignores them (e.g. "john.doe" and "johndoe" are considered equal).
- *gmail_remove_subaddress: true*: Normalizes addresses by removing "sub-addresses", which is the part following a "+" sign (e.g. "foo+bar@gmail.com" becomes "foo@gmail.com").
- *gmail_convert_googlemaildotcom: true*: Converts addresses with domain @googlemail.com to @gmail.com, as they're equivalent.
- *outlookdotcom_lowercase: true* - Outlook.com addresses (including Windows Live and Hotmail) are known to be case-insensitive, so this switch allows lowercasing them even when *all_lowercase* is set to false. Please note that when *all_lowercase* is true, Outlook.com addresses are lowercased regardless of the value of this setting.
- *outlookdotcom_remove_subaddress: true*: Normalizes addresses by removing "sub-addresses", which is the part following a "+" sign (e.g. "foo+bar@outlook.com" becomes "foo@outlook.com").
- *yahoo_lowercase: true* - Yahoo Mail addresses are known to be case-insensitive, so this switch allows lowercasing them even when *all_lowercase* is set to false. Please note that when *all_lowercase* is true, Yahoo Mail addresses are lowercased regardless of the value of this setting.
- *yahoo_remove_subaddress: true*: Normalizes addresses by removing "sub-addresses", which is the part following a "-" sign (e.g. "foo-bar@yahoo.com" becomes "foo@yahoo.com").
- *icloud_lowercase: true* - iCloud addresses (including MobileMe) are known to be case-insensitive, so this switch allows lowercasing them even when *all_lowercase* is set to false. Please note that when *all_lowercase* is true, iCloud addresses are lowercased regardless of the value of this setting.
- *icloud_remove_subaddress: true*: Normalizes addresses by removing "sub-addresses", which is the part following a "+" sign (e.g. "foo+bar@icloud.com" becomes "foo@icloud.com").
+**normalizeEmail(email [, options])** | canonicalize an email address. (This doesn't validate that the input is an email, if you want to validate the email use isEmail beforehand).
`options` is an object with the following keys and default values:
- *all_lowercase: true* - Transforms the local part (before the @ symbol) of all email addresses to lowercase. Please note that this may violate RFC 5321, which gives providers the possibility to treat the local part of email addresses in a case sensitive way (although in practice most - yet not all - providers don't). The domain part of the email address is always lowercased, as it is case insensitive per RFC 1035.
- *gmail_lowercase: true* - Gmail addresses are known to be case-insensitive, so this switch allows lowercasing them even when *all_lowercase* is set to false. Please note that when *all_lowercase* is true, Gmail addresses are lowercased regardless of the value of this setting.
- *gmail_remove_dots: true*: Removes dots from the local part of the email address, as Gmail ignores them (e.g. "john.doe" and "johndoe" are considered equal).
- *gmail_remove_subaddress: true*: Normalizes addresses by removing "sub-addresses", which is the part following a "+" sign (e.g. "foo+bar@gmail.com" becomes "foo@gmail.com").
- *gmail_convert_googlemaildotcom: true*: Converts addresses with domain @googlemail.com to @gmail.com, as they're equivalent.
- *outlookdotcom_lowercase: true* - Outlook.com addresses (including Windows Live and Hotmail) are known to be case-insensitive, so this switch allows lowercasing them even when *all_lowercase* is set to false. Please note that when *all_lowercase* is true, Outlook.com addresses are lowercased regardless of the value of this setting.
- *outlookdotcom_remove_subaddress: true*: Normalizes addresses by removing "sub-addresses", which is the part following a "+" sign (e.g. "foo+bar@outlook.com" becomes "foo@outlook.com").
- *yahoo_lowercase: true* - Yahoo Mail addresses are known to be case-insensitive, so this switch allows lowercasing them even when *all_lowercase* is set to false. Please note that when *all_lowercase* is true, Yahoo Mail addresses are lowercased regardless of the value of this setting.
- *yahoo_remove_subaddress: true*: Normalizes addresses by removing "sub-addresses", which is the part following a "-" sign (e.g. "foo-bar@yahoo.com" becomes "foo@yahoo.com").
- *icloud_lowercase: true* - iCloud addresses (including MobileMe) are known to be case-insensitive, so this switch allows lowercasing them even when *all_lowercase* is set to false. Please note that when *all_lowercase* is true, iCloud addresses are lowercased regardless of the value of this setting.
- *icloud_remove_subaddress: true*: Normalizes addresses by removing "sub-addresses", which is the part following a "+" sign (e.g. "foo+bar@icloud.com" becomes "foo@icloud.com").
**rtrim(input [, chars])** | trim characters from the right-side of the input.
**stripLow(input [, keep_new_lines])** | remove characters with a numerical value < 32 and 127, mostly control characters. If `keep_new_lines` is `true`, newline characters are preserved (`\n` and `\r`, hex `0xA` and `0xD`). Unicode-safe in JavaScript.
**toBoolean(input [, strict])** | convert the input string to a boolean. Everything except for `'0'`, `'false'` and `''` returns `true`. In strict mode only `'1'` and `'true'` return `true`.
@@ -131,77 +192,78 @@ Sanitizer | Description
**toFloat(input)** | convert the input string to a float, or `NaN` if the input is not a float.
**toInt(input [, radix])** | convert the input string to an integer, or `NaN` if the input is not an integer.
**trim(input [, chars])** | trim characters (whitespace by default) from both sides of the input.
+**unescape(input)** | replace HTML encoded entities with `<`, `>`, `&`, `'`, `"`, `` ` ``, `\` and `/`.
**whitelist(input, chars)** | remove characters that do not appear in the whitelist. The characters are used in a RegExp and so you will need to escape some chars, e.g. `whitelist(input, '\\[\\]')`.
### XSS Sanitization
-XSS sanitization was removed from the library in [2d5d6999](https://github.com/chriso/validator.js/commit/2d5d6999541add350fb396ef02dc42ca3215049e).
+XSS sanitization was removed from the library in [2d5d6999](https://github.com/validatorjs/validator.js/commit/2d5d6999541add350fb396ef02dc42ca3215049e).
For an alternative, have a look at Yahoo's [xss-filters library](https://github.com/yahoo/xss-filters) or at [DOMPurify](https://github.com/cure53/DOMPurify).
-## Contributing
+## Maintainers
-In general, we follow the "fork-and-pull" Git workflow.
-
-1. Fork the repo on GitHub
-2. Clone the project to your own machine
-3. Work on your fork
- 1. Make your changes and additions
- 2. Change or add tests if needed
- 3. Run tests and make sure they pass
- 4. Add changes to README.md if needed
-4. Commit changes to your own branch
-5. **Make sure** you merge the latest from "upstream" and resolve conflicts if there is any
-6. Push your work back up to your fork
-7. Submit a Pull request so that we can review your changes
-
-## Tests
-
-Tests are using mocha, to run the tests use:
-
-```sh
-$ npm test
-```
+- [chriso](https://github.com/chriso) - **Chris O'Hara** (author)
+- [profnandaa](https://github.com/profnandaa) - **Anthony Nandaa**
+- [rubiin](https://github.com/rubiin) - **Rubin Bhandari**
+- [wikirik](https://github.com/wikirik) - **Rik Smale**
+- [ezkemboi](https://github.com/ezkemboi) - **Ezrqn Kemboi**
+- [tux-tn](https://github.com/tux-tn) - **Sarhan Aissi**
## Reading
Remember, validating can be troublesome sometimes. See [A list of articles about programming assumptions commonly made that aren't true](https://github.com/jameslk/awesome-falsehoods).
-## License (MIT)
+## Contributing
-```
-Copyright (c) 2017 Chris O'Hara
-
-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.
-```
+We welcome contributions from the community! If you're interested in contributing to this project, please read our [Contribution Guide](CONTRIBUTING.md) to get started.
+
+## License
+
+This project is licensed under the [MIT](LICENSE). See the [LICENSE](LICENSE) file for details.
[downloads-image]: http://img.shields.io/npm/dm/validator.svg
[npm-url]: https://npmjs.org/package/validator
[npm-image]: http://img.shields.io/npm/v/validator.svg
-[travis-url]: https://travis-ci.org/chriso/validator.js
-[travis-image]: http://img.shields.io/travis/chriso/validator.js.svg
+[codecov-url]: https://codecov.io/gh/validatorjs/validator.js
+[codecov-image]: https://codecov.io/gh/validatorjs/validator.js/branch/master/graph/badge.svg
+
+[ci-url]: https://github.com/validatorjs/validator.js/actions?query=workflow%3ACI
+[ci-image]: https://github.com/validatorjs/validator.js/workflows/CI/badge.svg?branch=master
+
+[gitter-url]: https://gitter.im/validatorjs/community
+[gitter-image]: https://badges.gitter.im/validatorjs/community.svg
+
+[huntr-url]: https://huntr.dev/bounties/disclose/?target=https://github.com/validatorjs/validator.js
+[huntr-image]: https://cdn.huntr.dev/huntr_security_badge_mono.svg
[amd]: http://requirejs.org/docs/whyamd.html
[bower]: http://bower.io/
-[mongoid]: http://docs.mongodb.org/manual/reference/object-id/
+[Crockford Base32]: http://www.crockford.com/base32.html
+[Base64 URL Safe]: https://base64.guru/standards/base64url
+[Data URI Format]: https://developer.mozilla.org/en-US/docs/Web/HTTP/data_URIs
+[European Article Number]: https://en.wikipedia.org/wiki/International_Article_Number
+[Ethereum]: https://ethereum.org/
+[CSS Colors Level 4 Specification]: https://developer.mozilla.org/en-US/docs/Web/CSS/color_value
+[IMEI]: https://en.wikipedia.org/wiki/International_Mobile_Equipment_Identity
+[ISBN]: https://en.wikipedia.org/wiki/ISBN
[ISIN]: https://en.wikipedia.org/wiki/International_Securities_Identification_Number
+[ISO 639-1]: https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes
+[ISO 8601]: https://en.wikipedia.org/wiki/ISO_8601
+[ISO 15924]: https://en.wikipedia.org/wiki/ISO_15924
+[ISO 3166-1 alpha-2]: https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2
+[ISO 3166-1 alpha-3]: https://en.wikipedia.org/wiki/ISO_3166-1_alpha-3
+[ISO 3166-1 numeric]: https://en.wikipedia.org/wiki/ISO_3166-1_numeric
+[ISO 4217]: https://en.wikipedia.org/wiki/ISO_4217
+[ISRC]: https://en.wikipedia.org/wiki/International_Standard_Recording_Code
+[ISSN]: https://en.wikipedia.org/wiki/International_Standard_Serial_Number
+[Luhn Check]: https://en.wikipedia.org/wiki/Luhn_algorithm
+[Magnet URI Format]: https://en.wikipedia.org/wiki/Magnet_URI_scheme
+[Mailto URI Format]: https://en.wikipedia.org/wiki/Mailto
+[MIME Type]: https://en.wikipedia.org/wiki/Media_type
+[mongoid]: http://docs.mongodb.org/manual/reference/object-id/
+[RFC 3339]: https://tools.ietf.org/html/rfc3339
+[VAT Number]: https://en.wikipedia.org/wiki/VAT_identification_number
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 000000000..266d8d844
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,11 @@
+# Security Policy
+
+## Supported Versions
+
+In the case of a confirmed security issue, only the current version of validator is guaranteed to be patched.
+
+## Reporting a Vulnerability
+
+**Please don't disclose security-related issues publicly.**
+
+Report the security issue to the Node.js Security Working Group through the [HackerOne program](https://hackerone.com/nodejs-ecosystem) for ecosystem modules on npm, or to [Snyk Security Team](https://snyk.io/vulnerability-disclosure). They will help triage the security issue and work with all involved parties to remediate and release a fix.
diff --git a/bower.json b/bower.json
index fb98d0ebc..427670cfc 100644
--- a/bower.json
+++ b/bower.json
@@ -1,7 +1,7 @@
{
"name": "validator-js",
"main": "validator.js",
- "homepage": "https://github.com/chriso/validator.js",
+ "homepage": "https://github.com/validatorjs/validator.js",
"authors": [
"Chris O'Hara "
],
diff --git a/build-browser.js b/build-browser.js
index 4243caaca..c863bd399 100644
--- a/build-browser.js
+++ b/build-browser.js
@@ -1,28 +1,32 @@
-const pkg = require('./package.json');
-const fs = require('fs');
-const rollup = require('rollup').rollup;
-const babel = require('rollup-plugin-babel');
+/* eslint import/no-extraneous-dependencies: 0 */
+import fs from "fs";
+import { rollup } from "rollup";
+import babel from "rollup-plugin-babel";
+import babelPresetEnv from "@babel/preset-env";
+import pkg from "./package.json";
rollup({
- entry: 'src/index.js',
+ entry: "src/index.js",
plugins: [
babel({
- presets: ['es2015-rollup'],
+ presets: [[babelPresetEnv, { modules: false }]],
babelrc: false,
}),
],
-}).then(bundle => (
- bundle.write({
- dest: 'validator.js',
- format: 'umd',
- moduleName: pkg.name,
- banner: (
- '/*!\n' +
- String(fs.readFileSync('./LICENSE')).trim().split('\n').map(l => ` * ${l}`).join('\n') +
- '\n */'
- ),
- })
-)).catch(e => {
- process.stderr.write(e.message + '\n');
- process.exit(1);
-});
+})
+ .then((bundle) =>
+ bundle.write({
+ dest: "validator.js",
+ format: "umd",
+ moduleName: pkg.name,
+ banner: `/*!\n${String(fs.readFileSync("./LICENSE"))
+ .trim()
+ .split("\n")
+ .map((l) => ` * ${l}`)
+ .join("\n")}\n */`,
+ })
+ )
+ .catch((e) => {
+ process.stderr.write(`${e.message}\n`);
+ process.exit(1);
+ });
diff --git a/index.js b/index.js
deleted file mode 100644
index e4ed3876b..000000000
--- a/index.js
+++ /dev/null
@@ -1,346 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-
-var _toDate = require('./lib/toDate');
-
-var _toDate2 = _interopRequireDefault(_toDate);
-
-var _toFloat = require('./lib/toFloat');
-
-var _toFloat2 = _interopRequireDefault(_toFloat);
-
-var _toInt = require('./lib/toInt');
-
-var _toInt2 = _interopRequireDefault(_toInt);
-
-var _toBoolean = require('./lib/toBoolean');
-
-var _toBoolean2 = _interopRequireDefault(_toBoolean);
-
-var _equals = require('./lib/equals');
-
-var _equals2 = _interopRequireDefault(_equals);
-
-var _contains = require('./lib/contains');
-
-var _contains2 = _interopRequireDefault(_contains);
-
-var _matches = require('./lib/matches');
-
-var _matches2 = _interopRequireDefault(_matches);
-
-var _isEmail = require('./lib/isEmail');
-
-var _isEmail2 = _interopRequireDefault(_isEmail);
-
-var _isURL = require('./lib/isURL');
-
-var _isURL2 = _interopRequireDefault(_isURL);
-
-var _isMACAddress = require('./lib/isMACAddress');
-
-var _isMACAddress2 = _interopRequireDefault(_isMACAddress);
-
-var _isIP = require('./lib/isIP');
-
-var _isIP2 = _interopRequireDefault(_isIP);
-
-var _isFQDN = require('./lib/isFQDN');
-
-var _isFQDN2 = _interopRequireDefault(_isFQDN);
-
-var _isBoolean = require('./lib/isBoolean');
-
-var _isBoolean2 = _interopRequireDefault(_isBoolean);
-
-var _isAlpha = require('./lib/isAlpha');
-
-var _isAlpha2 = _interopRequireDefault(_isAlpha);
-
-var _isAlphanumeric = require('./lib/isAlphanumeric');
-
-var _isAlphanumeric2 = _interopRequireDefault(_isAlphanumeric);
-
-var _isNumeric = require('./lib/isNumeric');
-
-var _isNumeric2 = _interopRequireDefault(_isNumeric);
-
-var _isPort = require('./lib/isPort');
-
-var _isPort2 = _interopRequireDefault(_isPort);
-
-var _isLowercase = require('./lib/isLowercase');
-
-var _isLowercase2 = _interopRequireDefault(_isLowercase);
-
-var _isUppercase = require('./lib/isUppercase');
-
-var _isUppercase2 = _interopRequireDefault(_isUppercase);
-
-var _isAscii = require('./lib/isAscii');
-
-var _isAscii2 = _interopRequireDefault(_isAscii);
-
-var _isFullWidth = require('./lib/isFullWidth');
-
-var _isFullWidth2 = _interopRequireDefault(_isFullWidth);
-
-var _isHalfWidth = require('./lib/isHalfWidth');
-
-var _isHalfWidth2 = _interopRequireDefault(_isHalfWidth);
-
-var _isVariableWidth = require('./lib/isVariableWidth');
-
-var _isVariableWidth2 = _interopRequireDefault(_isVariableWidth);
-
-var _isMultibyte = require('./lib/isMultibyte');
-
-var _isMultibyte2 = _interopRequireDefault(_isMultibyte);
-
-var _isSurrogatePair = require('./lib/isSurrogatePair');
-
-var _isSurrogatePair2 = _interopRequireDefault(_isSurrogatePair);
-
-var _isInt = require('./lib/isInt');
-
-var _isInt2 = _interopRequireDefault(_isInt);
-
-var _isFloat = require('./lib/isFloat');
-
-var _isFloat2 = _interopRequireDefault(_isFloat);
-
-var _isDecimal = require('./lib/isDecimal');
-
-var _isDecimal2 = _interopRequireDefault(_isDecimal);
-
-var _isHexadecimal = require('./lib/isHexadecimal');
-
-var _isHexadecimal2 = _interopRequireDefault(_isHexadecimal);
-
-var _isDivisibleBy = require('./lib/isDivisibleBy');
-
-var _isDivisibleBy2 = _interopRequireDefault(_isDivisibleBy);
-
-var _isHexColor = require('./lib/isHexColor');
-
-var _isHexColor2 = _interopRequireDefault(_isHexColor);
-
-var _isISRC = require('./lib/isISRC');
-
-var _isISRC2 = _interopRequireDefault(_isISRC);
-
-var _isMD = require('./lib/isMD5');
-
-var _isMD2 = _interopRequireDefault(_isMD);
-
-var _isHash = require('./lib/isHash');
-
-var _isHash2 = _interopRequireDefault(_isHash);
-
-var _isJSON = require('./lib/isJSON');
-
-var _isJSON2 = _interopRequireDefault(_isJSON);
-
-var _isEmpty = require('./lib/isEmpty');
-
-var _isEmpty2 = _interopRequireDefault(_isEmpty);
-
-var _isLength = require('./lib/isLength');
-
-var _isLength2 = _interopRequireDefault(_isLength);
-
-var _isByteLength = require('./lib/isByteLength');
-
-var _isByteLength2 = _interopRequireDefault(_isByteLength);
-
-var _isUUID = require('./lib/isUUID');
-
-var _isUUID2 = _interopRequireDefault(_isUUID);
-
-var _isMongoId = require('./lib/isMongoId');
-
-var _isMongoId2 = _interopRequireDefault(_isMongoId);
-
-var _isAfter = require('./lib/isAfter');
-
-var _isAfter2 = _interopRequireDefault(_isAfter);
-
-var _isBefore = require('./lib/isBefore');
-
-var _isBefore2 = _interopRequireDefault(_isBefore);
-
-var _isIn = require('./lib/isIn');
-
-var _isIn2 = _interopRequireDefault(_isIn);
-
-var _isCreditCard = require('./lib/isCreditCard');
-
-var _isCreditCard2 = _interopRequireDefault(_isCreditCard);
-
-var _isISIN = require('./lib/isISIN');
-
-var _isISIN2 = _interopRequireDefault(_isISIN);
-
-var _isISBN = require('./lib/isISBN');
-
-var _isISBN2 = _interopRequireDefault(_isISBN);
-
-var _isISSN = require('./lib/isISSN');
-
-var _isISSN2 = _interopRequireDefault(_isISSN);
-
-var _isMobilePhone = require('./lib/isMobilePhone');
-
-var _isMobilePhone2 = _interopRequireDefault(_isMobilePhone);
-
-var _isCurrency = require('./lib/isCurrency');
-
-var _isCurrency2 = _interopRequireDefault(_isCurrency);
-
-var _isISO = require('./lib/isISO8601');
-
-var _isISO2 = _interopRequireDefault(_isISO);
-
-var _isISO31661Alpha = require('./lib/isISO31661Alpha2');
-
-var _isISO31661Alpha2 = _interopRequireDefault(_isISO31661Alpha);
-
-var _isBase = require('./lib/isBase64');
-
-var _isBase2 = _interopRequireDefault(_isBase);
-
-var _isDataURI = require('./lib/isDataURI');
-
-var _isDataURI2 = _interopRequireDefault(_isDataURI);
-
-var _isLatLong = require('./lib/isLatLong');
-
-var _isLatLong2 = _interopRequireDefault(_isLatLong);
-
-var _isPostalCode = require('./lib/isPostalCode');
-
-var _isPostalCode2 = _interopRequireDefault(_isPostalCode);
-
-var _ltrim = require('./lib/ltrim');
-
-var _ltrim2 = _interopRequireDefault(_ltrim);
-
-var _rtrim = require('./lib/rtrim');
-
-var _rtrim2 = _interopRequireDefault(_rtrim);
-
-var _trim = require('./lib/trim');
-
-var _trim2 = _interopRequireDefault(_trim);
-
-var _escape = require('./lib/escape');
-
-var _escape2 = _interopRequireDefault(_escape);
-
-var _unescape = require('./lib/unescape');
-
-var _unescape2 = _interopRequireDefault(_unescape);
-
-var _stripLow = require('./lib/stripLow');
-
-var _stripLow2 = _interopRequireDefault(_stripLow);
-
-var _whitelist = require('./lib/whitelist');
-
-var _whitelist2 = _interopRequireDefault(_whitelist);
-
-var _blacklist = require('./lib/blacklist');
-
-var _blacklist2 = _interopRequireDefault(_blacklist);
-
-var _isWhitelisted = require('./lib/isWhitelisted');
-
-var _isWhitelisted2 = _interopRequireDefault(_isWhitelisted);
-
-var _normalizeEmail = require('./lib/normalizeEmail');
-
-var _normalizeEmail2 = _interopRequireDefault(_normalizeEmail);
-
-var _toString = require('./lib/util/toString');
-
-var _toString2 = _interopRequireDefault(_toString);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-var version = '9.1.2';
-
-var validator = {
- version: version,
- toDate: _toDate2.default,
- toFloat: _toFloat2.default,
- toInt: _toInt2.default,
- toBoolean: _toBoolean2.default,
- equals: _equals2.default,
- contains: _contains2.default,
- matches: _matches2.default,
- isEmail: _isEmail2.default,
- isURL: _isURL2.default,
- isMACAddress: _isMACAddress2.default,
- isIP: _isIP2.default,
- isFQDN: _isFQDN2.default,
- isBoolean: _isBoolean2.default,
- isAlpha: _isAlpha2.default,
- isAlphanumeric: _isAlphanumeric2.default,
- isNumeric: _isNumeric2.default,
- isPort: _isPort2.default,
- isLowercase: _isLowercase2.default,
- isUppercase: _isUppercase2.default,
- isAscii: _isAscii2.default,
- isFullWidth: _isFullWidth2.default,
- isHalfWidth: _isHalfWidth2.default,
- isVariableWidth: _isVariableWidth2.default,
- isMultibyte: _isMultibyte2.default,
- isSurrogatePair: _isSurrogatePair2.default,
- isInt: _isInt2.default,
- isFloat: _isFloat2.default,
- isDecimal: _isDecimal2.default,
- isHexadecimal: _isHexadecimal2.default,
- isDivisibleBy: _isDivisibleBy2.default,
- isHexColor: _isHexColor2.default,
- isISRC: _isISRC2.default,
- isMD5: _isMD2.default,
- isHash: _isHash2.default,
- isJSON: _isJSON2.default,
- isEmpty: _isEmpty2.default,
- isLength: _isLength2.default,
- isByteLength: _isByteLength2.default,
- isUUID: _isUUID2.default,
- isMongoId: _isMongoId2.default,
- isAfter: _isAfter2.default,
- isBefore: _isBefore2.default,
- isIn: _isIn2.default,
- isCreditCard: _isCreditCard2.default,
- isISIN: _isISIN2.default,
- isISBN: _isISBN2.default,
- isISSN: _isISSN2.default,
- isMobilePhone: _isMobilePhone2.default,
- isPostalCode: _isPostalCode2.default,
- isCurrency: _isCurrency2.default,
- isISO8601: _isISO2.default,
- isISO31661Alpha2: _isISO31661Alpha2.default,
- isBase64: _isBase2.default,
- isDataURI: _isDataURI2.default,
- isLatLong: _isLatLong2.default,
- ltrim: _ltrim2.default,
- rtrim: _rtrim2.default,
- trim: _trim2.default,
- escape: _escape2.default,
- unescape: _unescape2.default,
- stripLow: _stripLow2.default,
- whitelist: _whitelist2.default,
- blacklist: _blacklist2.default,
- isWhitelisted: _isWhitelisted2.default,
- normalizeEmail: _normalizeEmail2.default,
- toString: _toString2.default
-};
-
-exports.default = validator;
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/alpha.js b/lib/alpha.js
deleted file mode 100644
index bfcd861ec..000000000
--- a/lib/alpha.js
+++ /dev/null
@@ -1,90 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-var alpha = exports.alpha = {
- 'en-US': /^[A-Z]+$/i,
- 'cs-CZ': /^[A-ZÁČĎÉĚÍŇÓŘŠŤÚŮÝŽ]+$/i,
- 'da-DK': /^[A-ZÆØÅ]+$/i,
- 'de-DE': /^[A-ZÄÖÜß]+$/i,
- 'es-ES': /^[A-ZÁÉÍÑÓÚÜ]+$/i,
- 'fr-FR': /^[A-ZÀÂÆÇÉÈÊËÏÎÔŒÙÛÜŸ]+$/i,
- 'it-IT': /^[A-ZÀÉÈÌÎÓÒÙ]+$/i,
- 'nb-NO': /^[A-ZÆØÅ]+$/i,
- 'nl-NL': /^[A-ZÁÉËÏÓÖÜÚ]+$/i,
- 'nn-NO': /^[A-ZÆØÅ]+$/i,
- 'hu-HU': /^[A-ZÁÉÍÓÖŐÚÜŰ]+$/i,
- 'pl-PL': /^[A-ZĄĆĘŚŁŃÓŻŹ]+$/i,
- 'pt-PT': /^[A-ZÃÁÀÂÇÉÊÍÕÓÔÚÜ]+$/i,
- 'ru-RU': /^[А-ЯЁ]+$/i,
- 'sr-RS@latin': /^[A-ZČĆŽŠĐ]+$/i,
- 'sr-RS': /^[А-ЯЂЈЉЊЋЏ]+$/i,
- 'sv-SE': /^[A-ZÅÄÖ]+$/i,
- 'tr-TR': /^[A-ZÇĞİıÖŞÜ]+$/i,
- 'uk-UA': /^[А-ЩЬЮЯЄIЇҐі]+$/i,
- ar: /^[ءآأؤإئابةتثجحخدذرزسشصضطظعغفقكلمنهوىيًٌٍَُِّْٰ]+$/
-};
-
-var alphanumeric = exports.alphanumeric = {
- 'en-US': /^[0-9A-Z]+$/i,
- 'cs-CZ': /^[0-9A-ZÁČĎÉĚÍŇÓŘŠŤÚŮÝŽ]+$/i,
- 'da-DK': /^[0-9A-ZÆØÅ]+$/i,
- 'de-DE': /^[0-9A-ZÄÖÜß]+$/i,
- 'es-ES': /^[0-9A-ZÁÉÍÑÓÚÜ]+$/i,
- 'fr-FR': /^[0-9A-ZÀÂÆÇÉÈÊËÏÎÔŒÙÛÜŸ]+$/i,
- 'it-IT': /^[0-9A-ZÀÉÈÌÎÓÒÙ]+$/i,
- 'hu-HU': /^[0-9A-ZÁÉÍÓÖŐÚÜŰ]+$/i,
- 'nb-NO': /^[0-9A-ZÆØÅ]+$/i,
- 'nl-NL': /^[0-9A-ZÁÉËÏÓÖÜÚ]+$/i,
- 'nn-NO': /^[0-9A-ZÆØÅ]+$/i,
- 'pl-PL': /^[0-9A-ZĄĆĘŚŁŃÓŻŹ]+$/i,
- 'pt-PT': /^[0-9A-ZÃÁÀÂÇÉÊÍÕÓÔÚÜ]+$/i,
- 'ru-RU': /^[0-9А-ЯЁ]+$/i,
- 'sr-RS@latin': /^[0-9A-ZČĆŽŠĐ]+$/i,
- 'sr-RS': /^[0-9А-ЯЂЈЉЊЋЏ]+$/i,
- 'sv-SE': /^[0-9A-ZÅÄÖ]+$/i,
- 'tr-TR': /^[0-9A-ZÇĞİıÖŞÜ]+$/i,
- 'uk-UA': /^[0-9А-ЩЬЮЯЄIЇҐі]+$/i,
- ar: /^[٠١٢٣٤٥٦٧٨٩0-9ءآأؤإئابةتثجحخدذرزسشصضطظعغفقكلمنهوىيًٌٍَُِّْٰ]+$/
-};
-
-var decimal = exports.decimal = {
- 'en-US': '.',
- ar: '٫'
-};
-
-var englishLocales = exports.englishLocales = ['AU', 'GB', 'HK', 'IN', 'NZ', 'ZA', 'ZM'];
-
-for (var locale, i = 0; i < englishLocales.length; i++) {
- locale = 'en-' + englishLocales[i];
- alpha[locale] = alpha['en-US'];
- alphanumeric[locale] = alphanumeric['en-US'];
- decimal[locale] = decimal['en-US'];
-}
-
-// Source: http://www.localeplanet.com/java/
-var arabicLocales = exports.arabicLocales = ['AE', 'BH', 'DZ', 'EG', 'IQ', 'JO', 'KW', 'LB', 'LY', 'MA', 'QM', 'QA', 'SA', 'SD', 'SY', 'TN', 'YE'];
-
-for (var _locale, _i = 0; _i < arabicLocales.length; _i++) {
- _locale = 'ar-' + arabicLocales[_i];
- alpha[_locale] = alpha.ar;
- alphanumeric[_locale] = alphanumeric.ar;
- decimal[_locale] = decimal.ar;
-}
-
-// Source: https://en.wikipedia.org/wiki/Decimal_mark
-var dotDecimal = exports.dotDecimal = [];
-var commaDecimal = exports.commaDecimal = ['cs-CZ', 'da-DK', 'de-DE', 'es-ES', 'fr-FR', 'it-IT', 'hu-HU', 'nb-NO', 'nn-NO', 'nl-NL', 'pl-Pl', 'pt-PT', 'ru-RU', 'sr-RS@latin', 'sr-RS', 'sv-SE', 'tr-TR', 'uk-UA'];
-
-for (var _i2 = 0; _i2 < dotDecimal.length; _i2++) {
- decimal[dotDecimal[_i2]] = decimal['en-US'];
-}
-
-for (var _i3 = 0; _i3 < commaDecimal.length; _i3++) {
- decimal[commaDecimal[_i3]] = ',';
-}
-
-alpha['pt-BR'] = alpha['pt-PT'];
-alphanumeric['pt-BR'] = alphanumeric['pt-PT'];
-decimal['pt-BR'] = decimal['pt-PT'];
\ No newline at end of file
diff --git a/lib/blacklist.js b/lib/blacklist.js
deleted file mode 100644
index 41ecdf0f6..000000000
--- a/lib/blacklist.js
+++ /dev/null
@@ -1,18 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = blacklist;
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-function blacklist(str, chars) {
- (0, _assertString2.default)(str);
- return str.replace(new RegExp('[' + chars + ']+', 'g'), '');
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/contains.js b/lib/contains.js
deleted file mode 100644
index 025e8012e..000000000
--- a/lib/contains.js
+++ /dev/null
@@ -1,22 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = contains;
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-var _toString = require('./util/toString');
-
-var _toString2 = _interopRequireDefault(_toString);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-function contains(str, elem) {
- (0, _assertString2.default)(str);
- return str.indexOf((0, _toString2.default)(elem)) >= 0;
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/equals.js b/lib/equals.js
deleted file mode 100644
index 30ac76a05..000000000
--- a/lib/equals.js
+++ /dev/null
@@ -1,18 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = equals;
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-function equals(str, comparison) {
- (0, _assertString2.default)(str);
- return str === comparison;
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/escape.js b/lib/escape.js
deleted file mode 100644
index abcd60774..000000000
--- a/lib/escape.js
+++ /dev/null
@@ -1,18 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = escape;
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-function escape(str) {
- (0, _assertString2.default)(str);
- return str.replace(/&/g, '&').replace(/"/g, '"').replace(/'/g, ''').replace(//g, '>').replace(/\//g, '/').replace(/\\/g, '\').replace(/`/g, '`');
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/isAfter.js b/lib/isAfter.js
deleted file mode 100644
index 9c84d67cf..000000000
--- a/lib/isAfter.js
+++ /dev/null
@@ -1,26 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = isAfter;
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-var _toDate = require('./toDate');
-
-var _toDate2 = _interopRequireDefault(_toDate);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-function isAfter(str) {
- var date = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : String(new Date());
-
- (0, _assertString2.default)(str);
- var comparison = (0, _toDate2.default)(date);
- var original = (0, _toDate2.default)(str);
- return !!(original && comparison && original > comparison);
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/isAlpha.js b/lib/isAlpha.js
deleted file mode 100644
index 4aff94c83..000000000
--- a/lib/isAlpha.js
+++ /dev/null
@@ -1,25 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = isAlpha;
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-var _alpha = require('./alpha');
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-function isAlpha(str) {
- var locale = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'en-US';
-
- (0, _assertString2.default)(str);
- if (locale in _alpha.alpha) {
- return _alpha.alpha[locale].test(str);
- }
- throw new Error('Invalid locale \'' + locale + '\'');
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/isAlphanumeric.js b/lib/isAlphanumeric.js
deleted file mode 100644
index 9fb36a799..000000000
--- a/lib/isAlphanumeric.js
+++ /dev/null
@@ -1,25 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = isAlphanumeric;
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-var _alpha = require('./alpha');
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-function isAlphanumeric(str) {
- var locale = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'en-US';
-
- (0, _assertString2.default)(str);
- if (locale in _alpha.alphanumeric) {
- return _alpha.alphanumeric[locale].test(str);
- }
- throw new Error('Invalid locale \'' + locale + '\'');
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/isAscii.js b/lib/isAscii.js
deleted file mode 100644
index 55171e844..000000000
--- a/lib/isAscii.js
+++ /dev/null
@@ -1,22 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = isAscii;
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-/* eslint-disable no-control-regex */
-var ascii = /^[\x00-\x7F]+$/;
-/* eslint-enable no-control-regex */
-
-function isAscii(str) {
- (0, _assertString2.default)(str);
- return ascii.test(str);
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/isBase64.js b/lib/isBase64.js
deleted file mode 100644
index 0b4a807f5..000000000
--- a/lib/isBase64.js
+++ /dev/null
@@ -1,25 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = isBase64;
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-var notBase64 = /[^A-Z0-9+\/=]/i;
-
-function isBase64(str) {
- (0, _assertString2.default)(str);
- var len = str.length;
- if (!len || len % 4 !== 0 || notBase64.test(str)) {
- return false;
- }
- var firstPaddingChar = str.indexOf('=');
- return firstPaddingChar === -1 || firstPaddingChar === len - 1 || firstPaddingChar === len - 2 && str[len - 1] === '=';
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/isBefore.js b/lib/isBefore.js
deleted file mode 100644
index b4e3cf625..000000000
--- a/lib/isBefore.js
+++ /dev/null
@@ -1,26 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = isBefore;
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-var _toDate = require('./toDate');
-
-var _toDate2 = _interopRequireDefault(_toDate);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-function isBefore(str) {
- var date = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : String(new Date());
-
- (0, _assertString2.default)(str);
- var comparison = (0, _toDate2.default)(date);
- var original = (0, _toDate2.default)(str);
- return !!(original && comparison && original < comparison);
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/isBoolean.js b/lib/isBoolean.js
deleted file mode 100644
index 7dbd26f8c..000000000
--- a/lib/isBoolean.js
+++ /dev/null
@@ -1,18 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = isBoolean;
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-function isBoolean(str) {
- (0, _assertString2.default)(str);
- return ['true', 'false', '1', '0'].indexOf(str) >= 0;
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/isByteLength.js b/lib/isByteLength.js
deleted file mode 100644
index 5ec897c6b..000000000
--- a/lib/isByteLength.js
+++ /dev/null
@@ -1,33 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-
-var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
-
-exports.default = isByteLength;
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-/* eslint-disable prefer-rest-params */
-function isByteLength(str, options) {
- (0, _assertString2.default)(str);
- var min = void 0;
- var max = void 0;
- if ((typeof options === 'undefined' ? 'undefined' : _typeof(options)) === 'object') {
- min = options.min || 0;
- max = options.max;
- } else {
- // backwards compatibility: isByteLength(str, min [, max])
- min = arguments[1];
- max = arguments[2];
- }
- var len = encodeURI(str).split(/%..|./).length - 1;
- return len >= min && (typeof max === 'undefined' || len <= max);
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/isCreditCard.js b/lib/isCreditCard.js
deleted file mode 100644
index 2e204ef5c..000000000
--- a/lib/isCreditCard.js
+++ /dev/null
@@ -1,45 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = isCreditCard;
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-/* eslint-disable max-len */
-var creditCard = /^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|(222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\d{3})\d{11}|62[0-9]{14})$/;
-/* eslint-enable max-len */
-
-function isCreditCard(str) {
- (0, _assertString2.default)(str);
- var sanitized = str.replace(/[- ]+/g, '');
- if (!creditCard.test(sanitized)) {
- return false;
- }
- var sum = 0;
- var digit = void 0;
- var tmpNum = void 0;
- var shouldDouble = void 0;
- for (var i = sanitized.length - 1; i >= 0; i--) {
- digit = sanitized.substring(i, i + 1);
- tmpNum = parseInt(digit, 10);
- if (shouldDouble) {
- tmpNum *= 2;
- if (tmpNum >= 10) {
- sum += tmpNum % 10 + 1;
- } else {
- sum += tmpNum;
- }
- } else {
- sum += tmpNum;
- }
- shouldDouble = !shouldDouble;
- }
- return !!(sum % 10 === 0 ? sanitized : false);
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/isCurrency.js b/lib/isCurrency.js
deleted file mode 100644
index 5be44ef75..000000000
--- a/lib/isCurrency.js
+++ /dev/null
@@ -1,92 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = isCurrency;
-
-var _merge = require('./util/merge');
-
-var _merge2 = _interopRequireDefault(_merge);
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-function currencyRegex(options) {
- var decimal_digits = '\\d{' + options.digits_after_decimal[0] + '}';
- options.digits_after_decimal.forEach(function (digit, index) {
- if (index !== 0) decimal_digits = decimal_digits + '|\\d{' + digit + '}';
- });
- var symbol = '(\\' + options.symbol.replace(/\./g, '\\.') + ')' + (options.require_symbol ? '' : '?'),
- negative = '-?',
- whole_dollar_amount_without_sep = '[1-9]\\d*',
- whole_dollar_amount_with_sep = '[1-9]\\d{0,2}(\\' + options.thousands_separator + '\\d{3})*',
- valid_whole_dollar_amounts = ['0', whole_dollar_amount_without_sep, whole_dollar_amount_with_sep],
- whole_dollar_amount = '(' + valid_whole_dollar_amounts.join('|') + ')?',
- decimal_amount = '(\\' + options.decimal_separator + '(' + decimal_digits + '))' + (options.require_decimal ? '' : '?');
- var pattern = whole_dollar_amount + (options.allow_decimal || options.require_decimal ? decimal_amount : '');
-
- // default is negative sign before symbol, but there are two other options (besides parens)
- if (options.allow_negatives && !options.parens_for_negatives) {
- if (options.negative_sign_after_digits) {
- pattern += negative;
- } else if (options.negative_sign_before_digits) {
- pattern = negative + pattern;
- }
- }
-
- // South African Rand, for example, uses R 123 (space) and R-123 (no space)
- if (options.allow_negative_sign_placeholder) {
- pattern = '( (?!\\-))?' + pattern;
- } else if (options.allow_space_after_symbol) {
- pattern = ' ?' + pattern;
- } else if (options.allow_space_after_digits) {
- pattern += '( (?!$))?';
- }
-
- if (options.symbol_after_digits) {
- pattern += symbol;
- } else {
- pattern = symbol + pattern;
- }
-
- if (options.allow_negatives) {
- if (options.parens_for_negatives) {
- pattern = '(\\(' + pattern + '\\)|' + pattern + ')';
- } else if (!(options.negative_sign_before_digits || options.negative_sign_after_digits)) {
- pattern = negative + pattern;
- }
- }
-
- // ensure there's a dollar and/or decimal amount, and that
- // it doesn't start with a space or a negative sign followed by a space
- return new RegExp('^(?!-? )(?=.*\\d)' + pattern + '$');
-}
-
-var default_currency_options = {
- symbol: '$',
- require_symbol: false,
- allow_space_after_symbol: false,
- symbol_after_digits: false,
- allow_negatives: true,
- parens_for_negatives: false,
- negative_sign_before_digits: false,
- negative_sign_after_digits: false,
- allow_negative_sign_placeholder: false,
- thousands_separator: ',',
- decimal_separator: '.',
- allow_decimal: true,
- require_decimal: false,
- digits_after_decimal: [2],
- allow_space_after_digits: false
-};
-
-function isCurrency(str, options) {
- (0, _assertString2.default)(str);
- options = (0, _merge2.default)(options, default_currency_options);
- return currencyRegex(options).test(str);
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/isDataURI.js b/lib/isDataURI.js
deleted file mode 100644
index 7ca9fae3c..000000000
--- a/lib/isDataURI.js
+++ /dev/null
@@ -1,20 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = isDataURI;
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-var dataURI = /^\s*data:([a-z]+\/[a-z0-9\-\+]+(;[a-z\-]+=[a-z0-9\-]+)?)?(;base64)?,[a-z0-9!\$&',\(\)\*\+,;=\-\._~:@\/\?%\s]*\s*$/i; // eslint-disable-line max-len
-
-function isDataURI(str) {
- (0, _assertString2.default)(str);
- return dataURI.test(str);
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/isDate.js b/lib/isDate.js
deleted file mode 100644
index 0e641637d..000000000
--- a/lib/isDate.js
+++ /dev/null
@@ -1,100 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = isDate;
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-var _isISO = require('./isISO8601');
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-function getTimezoneOffset(str) {
- var iso8601Parts = str.match(_isISO.iso8601);
- var timezone = void 0,
- sign = void 0,
- hours = void 0,
- minutes = void 0;
- if (!iso8601Parts) {
- str = str.toLowerCase();
- timezone = str.match(/(?:\s|gmt\s*)(-|\+)(\d{1,4})(\s|$)/);
- if (!timezone) {
- return str.indexOf('gmt') !== -1 ? 0 : null;
- }
- sign = timezone[1];
- var offset = timezone[2];
- if (offset.length === 3) {
- offset = '0' + offset;
- }
- if (offset.length <= 2) {
- hours = 0;
- minutes = parseInt(offset, 10);
- } else {
- hours = parseInt(offset.slice(0, 2), 10);
- minutes = parseInt(offset.slice(2, 4), 10);
- }
- } else {
- timezone = iso8601Parts[21];
- if (!timezone) {
- // if no hour/minute was provided, the date is GMT
- return !iso8601Parts[12] ? 0 : null;
- }
- if (timezone === 'z' || timezone === 'Z') {
- return 0;
- }
- sign = iso8601Parts[22];
- if (timezone.indexOf(':') !== -1) {
- hours = parseInt(iso8601Parts[23], 10);
- minutes = parseInt(iso8601Parts[24], 10);
- } else {
- hours = 0;
- minutes = parseInt(iso8601Parts[23], 10);
- }
- }
- return (hours * 60 + minutes) * (sign === '-' ? 1 : -1);
-}
-
-function isDate(str) {
- (0, _assertString2.default)(str);
- var normalizedDate = new Date(Date.parse(str));
- if (isNaN(normalizedDate)) {
- return false;
- }
-
- // normalizedDate is in the user's timezone. Apply the input
- // timezone offset to the date so that the year and day match
- // the input
- var timezoneOffset = getTimezoneOffset(str);
- if (timezoneOffset !== null) {
- var timezoneDifference = normalizedDate.getTimezoneOffset() - timezoneOffset;
- normalizedDate = new Date(normalizedDate.getTime() + 60000 * timezoneDifference);
- }
-
- var day = String(normalizedDate.getDate());
- var dayOrYear = void 0,
- dayOrYearMatches = void 0,
- year = void 0;
- // check for valid double digits that could be late days
- // check for all matches since a string like '12/23' is a valid date
- // ignore everything with nearby colons
- dayOrYearMatches = str.match(/(^|[^:\d])[23]\d([^T:\d]|$)/g);
- if (!dayOrYearMatches) {
- return true;
- }
- dayOrYear = dayOrYearMatches.map(function (digitString) {
- return digitString.match(/\d+/g)[0];
- }).join('/');
-
- year = String(normalizedDate.getFullYear()).slice(-2);
- if (dayOrYear === day || dayOrYear === year) {
- return true;
- } else if (dayOrYear === '' + day / year || dayOrYear === '' + year / day) {
- return true;
- }
- return false;
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/isDecimal.js b/lib/isDecimal.js
deleted file mode 100644
index 7a66dace5..000000000
--- a/lib/isDecimal.js
+++ /dev/null
@@ -1,41 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = isDecimal;
-
-var _merge = require('./util/merge');
-
-var _merge2 = _interopRequireDefault(_merge);
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-var _alpha = require('./alpha');
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-function decimalRegExp(options) {
- var regExp = new RegExp('^[-+]?([0-9]+)?(\\' + _alpha.decimal[options.locale] + '[0-9]{' + options.decimal_digits + '})' + (options.force_decimal ? '' : '?') + '$');
- return regExp;
-}
-
-var default_decimal_options = {
- force_decimal: false,
- decimal_digits: '1,',
- locale: 'en-US'
-};
-
-var blacklist = ['', '-', '+'];
-
-function isDecimal(str, options) {
- (0, _assertString2.default)(str);
- options = (0, _merge2.default)(options, default_decimal_options);
- if (options.locale in _alpha.decimal) {
- return !blacklist.includes(str.replace(/ /g, '')) && decimalRegExp(options).test(str);
- }
- throw new Error('Invalid locale \'' + options.locale + '\'');
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/isDivisibleBy.js b/lib/isDivisibleBy.js
deleted file mode 100644
index 133603999..000000000
--- a/lib/isDivisibleBy.js
+++ /dev/null
@@ -1,22 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = isDivisibleBy;
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-var _toFloat = require('./toFloat');
-
-var _toFloat2 = _interopRequireDefault(_toFloat);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-function isDivisibleBy(str, num) {
- (0, _assertString2.default)(str);
- return (0, _toFloat2.default)(str) % parseInt(num, 10) === 0;
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/isEmail.js b/lib/isEmail.js
deleted file mode 100644
index 0944805be..000000000
--- a/lib/isEmail.js
+++ /dev/null
@@ -1,89 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = isEmail;
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-var _merge = require('./util/merge');
-
-var _merge2 = _interopRequireDefault(_merge);
-
-var _isByteLength = require('./isByteLength');
-
-var _isByteLength2 = _interopRequireDefault(_isByteLength);
-
-var _isFQDN = require('./isFQDN');
-
-var _isFQDN2 = _interopRequireDefault(_isFQDN);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-var default_email_options = {
- allow_display_name: false,
- require_display_name: false,
- allow_utf8_local_part: true,
- require_tld: true
-};
-
-/* eslint-disable max-len */
-/* eslint-disable no-control-regex */
-var displayName = /^[a-z\d!#\$%&'\*\+\-\/=\?\^_`{\|}~\.\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+[a-z\d!#\$%&'\*\+\-\/=\?\^_`{\|}~\,\.\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF\s]*<(.+)>$/i;
-var emailUserPart = /^[a-z\d!#\$%&'\*\+\-\/=\?\^_`{\|}~]+$/i;
-var quotedEmailUser = /^([\s\x01-\x08\x0b\x0c\x0e-\x1f\x7f\x21\x23-\x5b\x5d-\x7e]|(\\[\x01-\x09\x0b\x0c\x0d-\x7f]))*$/i;
-var emailUserUtf8Part = /^[a-z\d!#\$%&'\*\+\-\/=\?\^_`{\|}~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+$/i;
-var quotedEmailUserUtf8 = /^([\s\x01-\x08\x0b\x0c\x0e-\x1f\x7f\x21\x23-\x5b\x5d-\x7e\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|(\\[\x01-\x09\x0b\x0c\x0d-\x7f\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))*$/i;
-/* eslint-enable max-len */
-/* eslint-enable no-control-regex */
-
-function isEmail(str, options) {
- (0, _assertString2.default)(str);
- options = (0, _merge2.default)(options, default_email_options);
-
- if (options.require_display_name || options.allow_display_name) {
- var display_email = str.match(displayName);
- if (display_email) {
- str = display_email[1];
- } else if (options.require_display_name) {
- return false;
- }
- }
-
- var parts = str.split('@');
- var domain = parts.pop();
- var user = parts.join('@');
-
- var lower_domain = domain.toLowerCase();
- if (lower_domain === 'gmail.com' || lower_domain === 'googlemail.com') {
- user = user.replace(/\./g, '').toLowerCase();
- }
-
- if (!(0, _isByteLength2.default)(user, { max: 64 }) || !(0, _isByteLength2.default)(domain, { max: 254 })) {
- return false;
- }
-
- if (!(0, _isFQDN2.default)(domain, { require_tld: options.require_tld })) {
- return false;
- }
-
- if (user[0] === '"') {
- user = user.slice(1, user.length - 1);
- return options.allow_utf8_local_part ? quotedEmailUserUtf8.test(user) : quotedEmailUser.test(user);
- }
-
- var pattern = options.allow_utf8_local_part ? emailUserUtf8Part : emailUserPart;
-
- var user_parts = user.split('.');
- for (var i = 0; i < user_parts.length; i++) {
- if (!pattern.test(user_parts[i])) {
- return false;
- }
- }
-
- return true;
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/isEmpty.js b/lib/isEmpty.js
deleted file mode 100644
index f3e39e4df..000000000
--- a/lib/isEmpty.js
+++ /dev/null
@@ -1,18 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = isEmpty;
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-function isEmpty(str) {
- (0, _assertString2.default)(str);
- return str.length === 0;
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/isFQDN.js b/lib/isFQDN.js
deleted file mode 100644
index 9ac148da5..000000000
--- a/lib/isFQDN.js
+++ /dev/null
@@ -1,61 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = isFQDN;
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-var _merge = require('./util/merge');
-
-var _merge2 = _interopRequireDefault(_merge);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-var default_fqdn_options = {
- require_tld: true,
- allow_underscores: false,
- allow_trailing_dot: false
-};
-
-function isFQDN(str, options) {
- (0, _assertString2.default)(str);
- options = (0, _merge2.default)(options, default_fqdn_options);
-
- /* Remove the optional trailing dot before checking validity */
- if (options.allow_trailing_dot && str[str.length - 1] === '.') {
- str = str.substring(0, str.length - 1);
- }
- var parts = str.split('.');
- if (options.require_tld) {
- var tld = parts.pop();
- if (!parts.length || !/^([a-z\u00a1-\uffff]{2,}|xn[a-z0-9-]{2,})$/i.test(tld)) {
- return false;
- }
- // disallow spaces
- if (/[\s\u2002-\u200B\u202F\u205F\u3000\uFEFF\uDB40\uDC20]/.test(tld)) {
- return false;
- }
- }
- for (var part, i = 0; i < parts.length; i++) {
- part = parts[i];
- if (options.allow_underscores) {
- part = part.replace(/_/g, '');
- }
- if (!/^[a-z\u00a1-\uffff0-9-]+$/i.test(part)) {
- return false;
- }
- // disallow full-width chars
- if (/[\uff01-\uff5e]/.test(part)) {
- return false;
- }
- if (part[0] === '-' || part[part.length - 1] === '-') {
- return false;
- }
- }
- return true;
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/isFloat.js b/lib/isFloat.js
deleted file mode 100644
index b81b2ae08..000000000
--- a/lib/isFloat.js
+++ /dev/null
@@ -1,25 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = isFloat;
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-var _alpha = require('./alpha');
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-function isFloat(str, options) {
- (0, _assertString2.default)(str);
- options = options || {};
- var float = new RegExp('^(?:[-+])?(?:[0-9]+)?(?:\\' + (options.locale ? _alpha.decimal[options.locale] : '.') + '[0-9]*)?(?:[eE][\\+\\-]?(?:[0-9]+))?$');
- if (str === '' || str === '.' || str === '-' || str === '+') {
- return false;
- }
- return float.test(str) && (!options.hasOwnProperty('min') || str >= options.min) && (!options.hasOwnProperty('max') || str <= options.max) && (!options.hasOwnProperty('lt') || str < options.lt) && (!options.hasOwnProperty('gt') || str > options.gt);
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/isFullWidth.js b/lib/isFullWidth.js
deleted file mode 100644
index 4215f8186..000000000
--- a/lib/isFullWidth.js
+++ /dev/null
@@ -1,20 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.fullWidth = undefined;
-exports.default = isFullWidth;
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-var fullWidth = exports.fullWidth = /[^\u0020-\u007E\uFF61-\uFF9F\uFFA0-\uFFDC\uFFE8-\uFFEE0-9a-zA-Z]/;
-
-function isFullWidth(str) {
- (0, _assertString2.default)(str);
- return fullWidth.test(str);
-}
\ No newline at end of file
diff --git a/lib/isHalfWidth.js b/lib/isHalfWidth.js
deleted file mode 100644
index 986c63245..000000000
--- a/lib/isHalfWidth.js
+++ /dev/null
@@ -1,20 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.halfWidth = undefined;
-exports.default = isHalfWidth;
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-var halfWidth = exports.halfWidth = /[\u0020-\u007E\uFF61-\uFF9F\uFFA0-\uFFDC\uFFE8-\uFFEE0-9a-zA-Z]/;
-
-function isHalfWidth(str) {
- (0, _assertString2.default)(str);
- return halfWidth.test(str);
-}
\ No newline at end of file
diff --git a/lib/isHash.js b/lib/isHash.js
deleted file mode 100644
index 32cc29b9f..000000000
--- a/lib/isHash.js
+++ /dev/null
@@ -1,35 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = isHash;
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-var lengths = {
- md5: 32,
- md4: 32,
- sha1: 40,
- sha256: 64,
- sha384: 96,
- sha512: 128,
- ripemd128: 32,
- ripemd160: 40,
- tiger128: 32,
- tiger160: 40,
- tiger192: 48,
- crc32: 8,
- crc32b: 8
-};
-
-function isHash(str, algorithm) {
- (0, _assertString2.default)(str);
- var hash = new RegExp('^[a-f0-9]{' + lengths[algorithm] + '}$');
- return hash.test(str);
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/isHexColor.js b/lib/isHexColor.js
deleted file mode 100644
index 90634710c..000000000
--- a/lib/isHexColor.js
+++ /dev/null
@@ -1,20 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = isHexColor;
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-var hexcolor = /^#?([0-9A-F]{3}|[0-9A-F]{6})$/i;
-
-function isHexColor(str) {
- (0, _assertString2.default)(str);
- return hexcolor.test(str);
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/isHexadecimal.js b/lib/isHexadecimal.js
deleted file mode 100644
index fb7808f9d..000000000
--- a/lib/isHexadecimal.js
+++ /dev/null
@@ -1,20 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = isHexadecimal;
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-var hexadecimal = /^[0-9A-F]+$/i;
-
-function isHexadecimal(str) {
- (0, _assertString2.default)(str);
- return hexadecimal.test(str);
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/isIP.js b/lib/isIP.js
deleted file mode 100644
index fce2ecbfb..000000000
--- a/lib/isIP.js
+++ /dev/null
@@ -1,81 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = isIP;
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-var ipv4Maybe = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/;
-var ipv6Block = /^[0-9A-F]{1,4}$/i;
-
-function isIP(str) {
- var version = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
-
- (0, _assertString2.default)(str);
- version = String(version);
- if (!version) {
- return isIP(str, 4) || isIP(str, 6);
- } else if (version === '4') {
- if (!ipv4Maybe.test(str)) {
- return false;
- }
- var parts = str.split('.').sort(function (a, b) {
- return a - b;
- });
- return parts[3] <= 255;
- } else if (version === '6') {
- var blocks = str.split(':');
- var foundOmissionBlock = false; // marker to indicate ::
-
- // At least some OS accept the last 32 bits of an IPv6 address
- // (i.e. 2 of the blocks) in IPv4 notation, and RFC 3493 says
- // that '::ffff:a.b.c.d' is valid for IPv4-mapped IPv6 addresses,
- // and '::a.b.c.d' is deprecated, but also valid.
- var foundIPv4TransitionBlock = isIP(blocks[blocks.length - 1], 4);
- var expectedNumberOfBlocks = foundIPv4TransitionBlock ? 7 : 8;
-
- if (blocks.length > expectedNumberOfBlocks) {
- return false;
- }
- // initial or final ::
- if (str === '::') {
- return true;
- } else if (str.substr(0, 2) === '::') {
- blocks.shift();
- blocks.shift();
- foundOmissionBlock = true;
- } else if (str.substr(str.length - 2) === '::') {
- blocks.pop();
- blocks.pop();
- foundOmissionBlock = true;
- }
-
- for (var i = 0; i < blocks.length; ++i) {
- // test for a :: which can not be at the string start/end
- // since those cases have been handled above
- if (blocks[i] === '' && i > 0 && i < blocks.length - 1) {
- if (foundOmissionBlock) {
- return false; // multiple :: in address
- }
- foundOmissionBlock = true;
- } else if (foundIPv4TransitionBlock && i === blocks.length - 1) {
- // it has been checked before that the last
- // block is a valid IPv4 address
- } else if (!ipv6Block.test(blocks[i])) {
- return false;
- }
- }
- if (foundOmissionBlock) {
- return blocks.length >= 1;
- }
- return blocks.length === expectedNumberOfBlocks;
- }
- return false;
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/isISBN.js b/lib/isISBN.js
deleted file mode 100644
index fccec2849..000000000
--- a/lib/isISBN.js
+++ /dev/null
@@ -1,57 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = isISBN;
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-var isbn10Maybe = /^(?:[0-9]{9}X|[0-9]{10})$/;
-var isbn13Maybe = /^(?:[0-9]{13})$/;
-var factor = [1, 3];
-
-function isISBN(str) {
- var version = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
-
- (0, _assertString2.default)(str);
- version = String(version);
- if (!version) {
- return isISBN(str, 10) || isISBN(str, 13);
- }
- var sanitized = str.replace(/[\s-]+/g, '');
- var checksum = 0;
- var i = void 0;
- if (version === '10') {
- if (!isbn10Maybe.test(sanitized)) {
- return false;
- }
- for (i = 0; i < 9; i++) {
- checksum += (i + 1) * sanitized.charAt(i);
- }
- if (sanitized.charAt(9) === 'X') {
- checksum += 10 * 10;
- } else {
- checksum += 10 * sanitized.charAt(9);
- }
- if (checksum % 11 === 0) {
- return !!sanitized;
- }
- } else if (version === '13') {
- if (!isbn13Maybe.test(sanitized)) {
- return false;
- }
- for (i = 0; i < 12; i++) {
- checksum += factor[i % 2] * sanitized.charAt(i);
- }
- if (sanitized.charAt(12) - (10 - checksum % 10) % 10 === 0) {
- return !!sanitized;
- }
- }
- return false;
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/isISIN.js b/lib/isISIN.js
deleted file mode 100644
index feb9725a7..000000000
--- a/lib/isISIN.js
+++ /dev/null
@@ -1,48 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = isISIN;
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-var isin = /^[A-Z]{2}[0-9A-Z]{9}[0-9]$/;
-
-function isISIN(str) {
- (0, _assertString2.default)(str);
- if (!isin.test(str)) {
- return false;
- }
-
- var checksumStr = str.replace(/[A-Z]/g, function (character) {
- return parseInt(character, 36);
- });
-
- var sum = 0;
- var digit = void 0;
- var tmpNum = void 0;
- var shouldDouble = true;
- for (var i = checksumStr.length - 2; i >= 0; i--) {
- digit = checksumStr.substring(i, i + 1);
- tmpNum = parseInt(digit, 10);
- if (shouldDouble) {
- tmpNum *= 2;
- if (tmpNum >= 10) {
- sum += tmpNum + 1;
- } else {
- sum += tmpNum;
- }
- } else {
- sum += tmpNum;
- }
- shouldDouble = !shouldDouble;
- }
-
- return parseInt(str.substr(str.length - 1), 10) === (10000 - sum) % 10;
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/isISO31661Alpha2.js b/lib/isISO31661Alpha2.js
deleted file mode 100644
index 1741cc13d..000000000
--- a/lib/isISO31661Alpha2.js
+++ /dev/null
@@ -1,21 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = isISO31661Alpha2;
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-// from https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2
-var validISO31661Alpha2CountriesCodes = ['AD', 'AE', 'AF', 'AG', 'AI', 'AL', 'AM', 'AO', 'AQ', 'AR', 'AS', 'AT', 'AU', 'AW', 'AX', 'AZ', 'BA', 'BB', 'BD', 'BE', 'BF', 'BG', 'BH', 'BI', 'BJ', 'BL', 'BM', 'BN', 'BO', 'BQ', 'BR', 'BS', 'BT', 'BV', 'BW', 'BY', 'BZ', 'CA', 'CC', 'CD', 'CF', 'CG', 'CH', 'CI', 'CK', 'CL', 'CM', 'CN', 'CO', 'CR', 'CU', 'CV', 'CW', 'CX', 'CY', 'CZ', 'DE', 'DJ', 'DK', 'DM', 'DO', 'DZ', 'EC', 'EE', 'EG', 'EH', 'ER', 'ES', 'ET', 'FI', 'FJ', 'FK', 'FM', 'FO', 'FR', 'GA', 'GB', 'GD', 'GE', 'GF', 'GG', 'GH', 'GI', 'GL', 'GM', 'GN', 'GP', 'GQ', 'GR', 'GS', 'GT', 'GU', 'GW', 'GY', 'HK', 'HM', 'HN', 'HR', 'HT', 'HU', 'ID', 'IE', 'IL', 'IM', 'IN', 'IO', 'IQ', 'IR', 'IS', 'IT', 'JE', 'JM', 'JO', 'JP', 'KE', 'KG', 'KH', 'KI', 'KM', 'KN', 'KP', 'KR', 'KW', 'KY', 'KZ', 'LA', 'LB', 'LC', 'LI', 'LK', 'LR', 'LS', 'LT', 'LU', 'LV', 'LY', 'MA', 'MC', 'MD', 'ME', 'MF', 'MG', 'MH', 'MK', 'ML', 'MM', 'MN', 'MO', 'MP', 'MQ', 'MR', 'MS', 'MT', 'MU', 'MV', 'MW', 'MX', 'MY', 'MZ', 'NA', 'NC', 'NE', 'NF', 'NG', 'NI', 'NL', 'NO', 'NP', 'NR', 'NU', 'NZ', 'OM', 'PA', 'PE', 'PF', 'PG', 'PH', 'PK', 'PL', 'PM', 'PN', 'PR', 'PS', 'PT', 'PW', 'PY', 'QA', 'RE', 'RO', 'RS', 'RU', 'RW', 'SA', 'SB', 'SC', 'SD', 'SE', 'SG', 'SH', 'SI', 'SJ', 'SK', 'SL', 'SM', 'SN', 'SO', 'SR', 'SS', 'ST', 'SV', 'SX', 'SY', 'SZ', 'TC', 'TD', 'TF', 'TG', 'TH', 'TJ', 'TK', 'TL', 'TM', 'TN', 'TO', 'TR', 'TT', 'TV', 'TW', 'TZ', 'UA', 'UG', 'UM', 'US', 'UY', 'UZ', 'VA', 'VC', 'VE', 'VG', 'VI', 'VN', 'VU', 'WF', 'WS', 'YE', 'YT', 'ZA', 'ZM', 'ZW'];
-
-function isISO31661Alpha2(str) {
- (0, _assertString2.default)(str);
- return validISO31661Alpha2CountriesCodes.includes(str.toUpperCase());
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/isISO8601.js b/lib/isISO8601.js
deleted file mode 100644
index e38f8611b..000000000
--- a/lib/isISO8601.js
+++ /dev/null
@@ -1,23 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = isISO8601;
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-/* eslint-disable max-len */
-// from http://goo.gl/0ejHHW
-var iso8601 = /^([\+-]?\d{4}(?!\d{2}\b))((-?)((0[1-9]|1[0-2])(\3([12]\d|0[1-9]|3[01]))?|W([0-4]\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\d|[12]\d{2}|3([0-5]\d|6[1-6])))([T\s]((([01]\d|2[0-3])((:?)[0-5]\d)?|24:?00)([\.,]\d+(?!:))?)?(\17[0-5]\d([\.,]\d+)?)?([zZ]|([\+-])([01]\d|2[0-3]):?([0-5]\d)?)?)?)?$/;
-/* eslint-enable max-len */
-
-function isISO8601(str) {
- (0, _assertString2.default)(str);
- return iso8601.test(str);
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/isISRC.js b/lib/isISRC.js
deleted file mode 100644
index b567cec43..000000000
--- a/lib/isISRC.js
+++ /dev/null
@@ -1,21 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = isISRC;
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-// see http://isrc.ifpi.org/en/isrc-standard/code-syntax
-var isrc = /^[A-Z]{2}[0-9A-Z]{3}\d{2}\d{5}$/;
-
-function isISRC(str) {
- (0, _assertString2.default)(str);
- return isrc.test(str);
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/isISSN.js b/lib/isISSN.js
deleted file mode 100644
index 79b05c04e..000000000
--- a/lib/isISSN.js
+++ /dev/null
@@ -1,58 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = isISSN;
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-var issn = '^\\d{4}-?\\d{3}[\\dX]$';
-
-function isISSN(str) {
- var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
-
- (0, _assertString2.default)(str);
- var testIssn = issn;
- testIssn = options.require_hyphen ? testIssn.replace('?', '') : testIssn;
- testIssn = options.case_sensitive ? new RegExp(testIssn) : new RegExp(testIssn, 'i');
- if (!testIssn.test(str)) {
- return false;
- }
- var issnDigits = str.replace('-', '');
- var position = 8;
- var checksum = 0;
- var _iteratorNormalCompletion = true;
- var _didIteratorError = false;
- var _iteratorError = undefined;
-
- try {
- for (var _iterator = issnDigits[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
- var digit = _step.value;
-
- var digitValue = digit.toUpperCase() === 'X' ? 10 : +digit;
- checksum += digitValue * position;
- --position;
- }
- } catch (err) {
- _didIteratorError = true;
- _iteratorError = err;
- } finally {
- try {
- if (!_iteratorNormalCompletion && _iterator.return) {
- _iterator.return();
- }
- } finally {
- if (_didIteratorError) {
- throw _iteratorError;
- }
- }
- }
-
- return checksum % 11 === 0;
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/isIn.js b/lib/isIn.js
deleted file mode 100644
index 14aecde40..000000000
--- a/lib/isIn.js
+++ /dev/null
@@ -1,39 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-
-var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
-
-exports.default = isIn;
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-var _toString = require('./util/toString');
-
-var _toString2 = _interopRequireDefault(_toString);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-function isIn(str, options) {
- (0, _assertString2.default)(str);
- var i = void 0;
- if (Object.prototype.toString.call(options) === '[object Array]') {
- var array = [];
- for (i in options) {
- if ({}.hasOwnProperty.call(options, i)) {
- array[i] = (0, _toString2.default)(options[i]);
- }
- }
- return array.indexOf(str) >= 0;
- } else if ((typeof options === 'undefined' ? 'undefined' : _typeof(options)) === 'object') {
- return options.hasOwnProperty(str);
- } else if (options && typeof options.indexOf === 'function') {
- return options.indexOf(str) >= 0;
- }
- return false;
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/isInt.js b/lib/isInt.js
deleted file mode 100644
index 45a136700..000000000
--- a/lib/isInt.js
+++ /dev/null
@@ -1,33 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = isInt;
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-var int = /^(?:[-+]?(?:0|[1-9][0-9]*))$/;
-var intLeadingZeroes = /^[-+]?[0-9]+$/;
-
-function isInt(str, options) {
- (0, _assertString2.default)(str);
- options = options || {};
-
- // Get the regex to use for testing, based on whether
- // leading zeroes are allowed or not.
- var regex = options.hasOwnProperty('allow_leading_zeroes') && !options.allow_leading_zeroes ? int : intLeadingZeroes;
-
- // Check min/max/lt/gt
- var minCheckPassed = !options.hasOwnProperty('min') || str >= options.min;
- var maxCheckPassed = !options.hasOwnProperty('max') || str <= options.max;
- var ltCheckPassed = !options.hasOwnProperty('lt') || str < options.lt;
- var gtCheckPassed = !options.hasOwnProperty('gt') || str > options.gt;
-
- return regex.test(str) && minCheckPassed && maxCheckPassed && ltCheckPassed && gtCheckPassed;
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/isJSON.js b/lib/isJSON.js
deleted file mode 100644
index 3d432c7e0..000000000
--- a/lib/isJSON.js
+++ /dev/null
@@ -1,25 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-
-var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
-
-exports.default = isJSON;
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-function isJSON(str) {
- (0, _assertString2.default)(str);
- try {
- var obj = JSON.parse(str);
- return !!obj && (typeof obj === 'undefined' ? 'undefined' : _typeof(obj)) === 'object';
- } catch (e) {/* ignore */}
- return false;
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/isLatLong.js b/lib/isLatLong.js
deleted file mode 100644
index 230d9ad58..000000000
--- a/lib/isLatLong.js
+++ /dev/null
@@ -1,23 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-
-exports.default = function (str) {
- (0, _assertString2.default)(str);
- if (!str.includes(',')) return false;
- var pair = str.split(',');
- return lat.test(pair[0]) && long.test(pair[1]);
-};
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-var lat = /^\(?[+-]?(90(\.0+)?|[1-8]?\d(\.\d+)?)$/;
-var long = /^\s?[+-]?(180(\.0+)?|1[0-7]\d(\.\d+)?|\d{1,2}(\.\d+)?)\)?$/;
-
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/isLength.js b/lib/isLength.js
deleted file mode 100644
index 432819029..000000000
--- a/lib/isLength.js
+++ /dev/null
@@ -1,34 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-
-var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
-
-exports.default = isLength;
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-/* eslint-disable prefer-rest-params */
-function isLength(str, options) {
- (0, _assertString2.default)(str);
- var min = void 0;
- var max = void 0;
- if ((typeof options === 'undefined' ? 'undefined' : _typeof(options)) === 'object') {
- min = options.min || 0;
- max = options.max;
- } else {
- // backwards compatibility: isLength(str, min [, max])
- min = arguments[1];
- max = arguments[2];
- }
- var surrogatePairs = str.match(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g) || [];
- var len = str.length - surrogatePairs.length;
- return len >= min && (typeof max === 'undefined' || len <= max);
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/isLowercase.js b/lib/isLowercase.js
deleted file mode 100644
index 556ec6712..000000000
--- a/lib/isLowercase.js
+++ /dev/null
@@ -1,18 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = isLowercase;
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-function isLowercase(str) {
- (0, _assertString2.default)(str);
- return str === str.toLowerCase();
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/isMACAddress.js b/lib/isMACAddress.js
deleted file mode 100644
index 16d1631de..000000000
--- a/lib/isMACAddress.js
+++ /dev/null
@@ -1,20 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = isMACAddress;
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-var macAddress = /^([0-9a-fA-F][0-9a-fA-F]:){5}([0-9a-fA-F][0-9a-fA-F])$/;
-
-function isMACAddress(str) {
- (0, _assertString2.default)(str);
- return macAddress.test(str);
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/isMD5.js b/lib/isMD5.js
deleted file mode 100644
index 6de9d17fc..000000000
--- a/lib/isMD5.js
+++ /dev/null
@@ -1,20 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = isMD5;
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-var md5 = /^[a-f0-9]{32}$/;
-
-function isMD5(str) {
- (0, _assertString2.default)(str);
- return md5.test(str);
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/isMobilePhone.js b/lib/isMobilePhone.js
deleted file mode 100644
index 81b85504e..000000000
--- a/lib/isMobilePhone.js
+++ /dev/null
@@ -1,96 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = isMobilePhone;
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-/* eslint-disable max-len */
-var phones = {
- 'ar-AE': /^((\+?971)|0)?5[024568]\d{7}$/,
- 'ar-DZ': /^(\+?213|0)(5|6|7)\d{8}$/,
- 'ar-EG': /^((\+?20)|0)?1[012]\d{8}$/,
- 'ar-JO': /^(\+?962|0)?7[789]\d{7}$/,
- 'ar-SA': /^(!?(\+?966)|0)?5\d{8}$/,
- 'ar-SY': /^(!?(\+?963)|0)?9\d{8}$/,
- 'cs-CZ': /^(\+?420)? ?[1-9][0-9]{2} ?[0-9]{3} ?[0-9]{3}$/,
- 'da-DK': /^(\+?45)?\s?\d{2}\s?\d{2}\s?\d{2}\s?\d{2}$/,
- 'de-DE': /^(\+?49[ \.\-])?([\(]{1}[0-9]{1,6}[\)])?([0-9 \.\-\/]{3,20})((x|ext|extension)[ ]?[0-9]{1,4})?$/,
- 'el-GR': /^(\+?30)?(69\d{8})$/,
- 'en-AU': /^(\+?61|0)4\d{8}$/,
- 'en-GB': /^(\+?44|0)7\d{9}$/,
- 'en-HK': /^(\+?852\-?)?[456789]\d{3}\-?\d{4}$/,
- 'en-IN': /^(\+?91|0)?[789]\d{9}$/,
- 'en-KE': /^(\+?254|0)?[7]\d{8}$/,
- 'en-NG': /^(\+?234|0)?[789]\d{9}$/,
- 'en-NZ': /^(\+?64|0)2\d{7,9}$/,
- 'en-PK': /^((\+92)|(0092))-{0,1}\d{3}-{0,1}\d{7}$|^\d{11}$|^\d{4}-\d{7}$/,
- 'en-RW': /^(\+?250|0)?[7]\d{8}$/,
- 'en-SG': /^(\+65)?[89]\d{7}$/,
- 'en-TZ': /^(\+?255|0)?[67]\d{8}$/,
- 'en-UG': /^(\+?256|0)?[7]\d{8}$/,
- 'en-US': /^(\+?1)?[2-9]\d{2}[2-9](?!11)\d{6}$/,
- 'en-ZA': /^(\+?27|0)\d{9}$/,
- 'en-ZM': /^(\+?26)?09[567]\d{7}$/,
- 'es-ES': /^(\+?34)?(6\d{1}|7[1234])\d{7}$/,
- 'et-EE': /^(\+?372)?\s?(5|8[1-4])\s?([0-9]\s?){6,7}$/,
- 'fa-IR': /^(\+?98[\-\s]?|0)9[0-39]\d[\-\s]?\d{3}[\-\s]?\d{4}$/,
- 'fi-FI': /^(\+?358|0)\s?(4(0|1|2|4|5|6)?|50)\s?(\d\s?){4,8}\d$/,
- 'fo-FO': /^(\+?298)?\s?\d{2}\s?\d{2}\s?\d{2}$/,
- 'fr-FR': /^(\+?33|0)[67]\d{8}$/,
- 'he-IL': /^(\+972|0)([23489]|5[0248]|77)[1-9]\d{6}/,
- 'hu-HU': /^(\+?36)(20|30|70)\d{7}$/,
- 'id-ID': /^(\+?62|0[1-9])[\s|\d]+$/,
- 'it-IT': /^(\+?39)?\s?3\d{2} ?\d{6,7}$/,
- 'ja-JP': /^(\+?81|0)[789]0[ \-]?[1-9]\d{2}[ \-]?\d{5}$/,
- 'kl-GL': /^(\+?299)?\s?\d{2}\s?\d{2}\s?\d{2}$/,
- 'ko-KR': /^((\+?82)[ \-]?)?0?1([0|1|6|7|8|9]{1})[ \-]?\d{3,4}[ \-]?\d{4}$/,
- 'lt-LT': /^(\+370|8)\d{8}$/,
- 'ms-MY': /^(\+?6?01){1}(([145]{1}(\-|\s)?\d{7,8})|([236789]{1}(\s|\-)?\d{7}))$/,
- 'nb-NO': /^(\+?47)?[49]\d{7}$/,
- 'nl-BE': /^(\+?32|0)4?\d{8}$/,
- 'nn-NO': /^(\+?47)?[49]\d{7}$/,
- 'pl-PL': /^(\+?48)? ?[5-8]\d ?\d{3} ?\d{2} ?\d{2}$/,
- 'pt-BR': /^(\+?55|0)\-?[1-9]{2}\-?[2-9]{1}\d{3,4}\-?\d{4}$/,
- 'pt-PT': /^(\+?351)?9[1236]\d{7}$/,
- 'ro-RO': /^(\+?4?0)\s?7\d{2}(\/|\s|\.|\-)?\d{3}(\s|\.|\-)?\d{3}$/,
- 'ru-RU': /^(\+?7|8)?9\d{9}$/,
- 'sk-SK': /^(\+?421)? ?[1-9][0-9]{2} ?[0-9]{3} ?[0-9]{3}$/,
- 'sr-RS': /^(\+3816|06)[- \d]{5,9}$/,
- 'tr-TR': /^(\+?90|0)?5\d{9}$/,
- 'uk-UA': /^(\+?38|8)?0\d{9}$/,
- 'vi-VN': /^(\+?84|0)?((1(2([0-9])|6([2-9])|88|99))|(9((?!5)[0-9])))([0-9]{7})$/,
- 'zh-CN': /^(\+?0?86\-?)?1[345789]\d{9}$/,
- 'zh-TW': /^(\+?886\-?|0)?9\d{8}$/
-};
-/* eslint-enable max-len */
-
-// aliases
-phones['en-CA'] = phones['en-US'];
-phones['fr-BE'] = phones['nl-BE'];
-phones['zh-HK'] = phones['en-HK'];
-
-function isMobilePhone(str, locale) {
- (0, _assertString2.default)(str);
- if (locale in phones) {
- return phones[locale].test(str);
- } else if (locale === 'any') {
- for (var key in phones) {
- if (phones.hasOwnProperty(key)) {
- var phone = phones[key];
- if (phone.test(str)) {
- return true;
- }
- }
- }
- return false;
- }
- throw new Error('Invalid locale \'' + locale + '\'');
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/isMongoId.js b/lib/isMongoId.js
deleted file mode 100644
index 62646bf41..000000000
--- a/lib/isMongoId.js
+++ /dev/null
@@ -1,22 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = isMongoId;
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-var _isHexadecimal = require('./isHexadecimal');
-
-var _isHexadecimal2 = _interopRequireDefault(_isHexadecimal);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-function isMongoId(str) {
- (0, _assertString2.default)(str);
- return (0, _isHexadecimal2.default)(str) && str.length === 24;
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/isMultibyte.js b/lib/isMultibyte.js
deleted file mode 100644
index 3411baab0..000000000
--- a/lib/isMultibyte.js
+++ /dev/null
@@ -1,22 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = isMultibyte;
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-/* eslint-disable no-control-regex */
-var multibyte = /[^\x00-\x7F]/;
-/* eslint-enable no-control-regex */
-
-function isMultibyte(str) {
- (0, _assertString2.default)(str);
- return multibyte.test(str);
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/isNumeric.js b/lib/isNumeric.js
deleted file mode 100644
index f3fde3046..000000000
--- a/lib/isNumeric.js
+++ /dev/null
@@ -1,20 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = isNumeric;
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-var numeric = /^[-+]?[0-9]+$/;
-
-function isNumeric(str) {
- (0, _assertString2.default)(str);
- return numeric.test(str);
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/isPort.js b/lib/isPort.js
deleted file mode 100644
index 66e119656..000000000
--- a/lib/isPort.js
+++ /dev/null
@@ -1,17 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = isPort;
-
-var _isInt = require('./isInt');
-
-var _isInt2 = _interopRequireDefault(_isInt);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-function isPort(str) {
- return (0, _isInt2.default)(str, { min: 0, max: 65535 });
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/isPostalCode.js b/lib/isPostalCode.js
deleted file mode 100644
index dfd0b255e..000000000
--- a/lib/isPostalCode.js
+++ /dev/null
@@ -1,75 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.locales = undefined;
-
-exports.default = function (str, locale) {
- (0, _assertString2.default)(str);
- if (locale in patterns) {
- return patterns[locale].test(str);
- } else if (locale === 'any') {
- for (var key in patterns) {
- if (patterns.hasOwnProperty(key)) {
- var pattern = patterns[key];
- if (pattern.test(str)) {
- return true;
- }
- }
- }
- return false;
- }
- throw new Error('Invalid locale \'' + locale + '\'');
-};
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-// common patterns
-var threeDigit = /^\d{3}$/;
-var fourDigit = /^\d{4}$/;
-var fiveDigit = /^\d{5}$/;
-var sixDigit = /^\d{6}$/;
-
-var patterns = {
- AT: fourDigit,
- AU: fourDigit,
- BE: fourDigit,
- CA: /^[ABCEGHJKLMNPRSTVXY]\d[ABCEGHJ-NPRSTV-Z][\s\-]?\d[ABCEGHJ-NPRSTV-Z]\d$/i,
- CH: fourDigit,
- CZ: /^\d{3}\s?\d{2}$/,
- DE: fiveDigit,
- DK: fourDigit,
- DZ: fiveDigit,
- ES: fiveDigit,
- FI: fiveDigit,
- FR: /^\d{2}\s?\d{3}$/,
- GB: /^(gir\s?0aa|[a-z]{1,2}\d[\da-z]?\s?(\d[a-z]{2})?)$/i,
- GR: /^\d{3}\s?\d{2}$/,
- IL: fiveDigit,
- IN: sixDigit,
- IS: threeDigit,
- IT: fiveDigit,
- JP: /^\d{3}\-\d{4}$/,
- KE: fiveDigit,
- LI: /^(948[5-9]|949[0-7])$/,
- MX: fiveDigit,
- NL: /^\d{4}\s?[a-z]{2}$/i,
- NO: fourDigit,
- PL: /^\d{2}\-\d{3}$/,
- PT: /^\d{4}(\-\d{3})?$/,
- RO: sixDigit,
- RU: sixDigit,
- SA: fiveDigit,
- SE: /^\d{3}\s?\d{2}$/,
- TW: /^\d{3}(\d{2})?$/,
- US: /^\d{5}(-\d{4})?$/,
- ZA: fourDigit,
- ZM: fiveDigit
-};
-
-var locales = exports.locales = Object.keys(patterns);
\ No newline at end of file
diff --git a/lib/isSurrogatePair.js b/lib/isSurrogatePair.js
deleted file mode 100644
index d8c7a9d2d..000000000
--- a/lib/isSurrogatePair.js
+++ /dev/null
@@ -1,20 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = isSurrogatePair;
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-var surrogatePair = /[\uD800-\uDBFF][\uDC00-\uDFFF]/;
-
-function isSurrogatePair(str) {
- (0, _assertString2.default)(str);
- return surrogatePair.test(str);
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/isURL.js b/lib/isURL.js
deleted file mode 100644
index 03babd3d0..000000000
--- a/lib/isURL.js
+++ /dev/null
@@ -1,147 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = isURL;
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-var _isFQDN = require('./isFQDN');
-
-var _isFQDN2 = _interopRequireDefault(_isFQDN);
-
-var _isIP = require('./isIP');
-
-var _isIP2 = _interopRequireDefault(_isIP);
-
-var _merge = require('./util/merge');
-
-var _merge2 = _interopRequireDefault(_merge);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-var default_url_options = {
- protocols: ['http', 'https', 'ftp'],
- require_tld: true,
- require_protocol: false,
- require_host: true,
- require_valid_protocol: true,
- allow_underscores: false,
- allow_trailing_dot: false,
- allow_protocol_relative_urls: false
-};
-
-var wrapped_ipv6 = /^\[([^\]]+)\](?::([0-9]+))?$/;
-
-function isRegExp(obj) {
- return Object.prototype.toString.call(obj) === '[object RegExp]';
-}
-
-function checkHost(host, matches) {
- for (var i = 0; i < matches.length; i++) {
- var match = matches[i];
- if (host === match || isRegExp(match) && match.test(host)) {
- return true;
- }
- }
- return false;
-}
-
-function isURL(url, options) {
- (0, _assertString2.default)(url);
- if (!url || url.length >= 2083 || /[\s<>]/.test(url)) {
- return false;
- }
- if (url.indexOf('mailto:') === 0) {
- return false;
- }
- options = (0, _merge2.default)(options, default_url_options);
- var protocol = void 0,
- auth = void 0,
- host = void 0,
- hostname = void 0,
- port = void 0,
- port_str = void 0,
- split = void 0,
- ipv6 = void 0;
-
- split = url.split('#');
- url = split.shift();
-
- split = url.split('?');
- url = split.shift();
-
- split = url.split('://');
- if (split.length > 1) {
- protocol = split.shift();
- if (options.require_valid_protocol && options.protocols.indexOf(protocol) === -1) {
- return false;
- }
- } else if (options.require_protocol) {
- return false;
- } else if (options.allow_protocol_relative_urls && url.substr(0, 2) === '//') {
- split[0] = url.substr(2);
- }
- url = split.join('://');
-
- if (url === '') {
- return false;
- }
-
- split = url.split('/');
- url = split.shift();
-
- if (url === '' && !options.require_host) {
- return true;
- }
-
- split = url.split('@');
- if (split.length > 1) {
- auth = split.shift();
- if (auth.indexOf(':') >= 0 && auth.split(':').length > 2) {
- return false;
- }
- }
- hostname = split.join('@');
-
- port_str = null;
- ipv6 = null;
- var ipv6_match = hostname.match(wrapped_ipv6);
- if (ipv6_match) {
- host = '';
- ipv6 = ipv6_match[1];
- port_str = ipv6_match[2] || null;
- } else {
- split = hostname.split(':');
- host = split.shift();
- if (split.length) {
- port_str = split.join(':');
- }
- }
-
- if (port_str !== null) {
- port = parseInt(port_str, 10);
- if (!/^[0-9]+$/.test(port_str) || port <= 0 || port > 65535) {
- return false;
- }
- }
-
- if (!(0, _isIP2.default)(host) && !(0, _isFQDN2.default)(host, options) && (!ipv6 || !(0, _isIP2.default)(ipv6, 6))) {
- return false;
- }
-
- host = host || ipv6;
-
- if (options.host_whitelist && !checkHost(host, options.host_whitelist)) {
- return false;
- }
- if (options.host_blacklist && checkHost(host, options.host_blacklist)) {
- return false;
- }
-
- return true;
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/isUUID.js b/lib/isUUID.js
deleted file mode 100644
index d37414457..000000000
--- a/lib/isUUID.js
+++ /dev/null
@@ -1,28 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = isUUID;
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-var uuid = {
- 3: /^[0-9A-F]{8}-[0-9A-F]{4}-3[0-9A-F]{3}-[0-9A-F]{4}-[0-9A-F]{12}$/i,
- 4: /^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i,
- 5: /^[0-9A-F]{8}-[0-9A-F]{4}-5[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i,
- all: /^[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}$/i
-};
-
-function isUUID(str) {
- var version = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'all';
-
- (0, _assertString2.default)(str);
- var pattern = uuid[version];
- return pattern && pattern.test(str);
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/isUppercase.js b/lib/isUppercase.js
deleted file mode 100644
index c4f11ebdb..000000000
--- a/lib/isUppercase.js
+++ /dev/null
@@ -1,18 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = isUppercase;
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-function isUppercase(str) {
- (0, _assertString2.default)(str);
- return str === str.toUpperCase();
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/isVariableWidth.js b/lib/isVariableWidth.js
deleted file mode 100644
index 5b1cf1549..000000000
--- a/lib/isVariableWidth.js
+++ /dev/null
@@ -1,22 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = isVariableWidth;
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-var _isFullWidth = require('./isFullWidth');
-
-var _isHalfWidth = require('./isHalfWidth');
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-function isVariableWidth(str) {
- (0, _assertString2.default)(str);
- return _isFullWidth.fullWidth.test(str) && _isHalfWidth.halfWidth.test(str);
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/isWhitelisted.js b/lib/isWhitelisted.js
deleted file mode 100644
index 1a073561e..000000000
--- a/lib/isWhitelisted.js
+++ /dev/null
@@ -1,23 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = isWhitelisted;
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-function isWhitelisted(str, chars) {
- (0, _assertString2.default)(str);
- for (var i = str.length - 1; i >= 0; i--) {
- if (chars.indexOf(str[i]) === -1) {
- return false;
- }
- }
- return true;
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/ltrim.js b/lib/ltrim.js
deleted file mode 100644
index 58b2e3db4..000000000
--- a/lib/ltrim.js
+++ /dev/null
@@ -1,19 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = ltrim;
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-function ltrim(str, chars) {
- (0, _assertString2.default)(str);
- var pattern = chars ? new RegExp('^[' + chars + ']+', 'g') : /^\s+/g;
- return str.replace(pattern, '');
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/matches.js b/lib/matches.js
deleted file mode 100644
index 43f788489..000000000
--- a/lib/matches.js
+++ /dev/null
@@ -1,21 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = matches;
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-function matches(str, pattern, modifiers) {
- (0, _assertString2.default)(str);
- if (Object.prototype.toString.call(pattern) !== '[object RegExp]') {
- pattern = new RegExp(pattern, modifiers);
- }
- return pattern.test(str);
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/normalizeEmail.js b/lib/normalizeEmail.js
deleted file mode 100644
index fb448b32c..000000000
--- a/lib/normalizeEmail.js
+++ /dev/null
@@ -1,129 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = normalizeEmail;
-
-var _merge = require('./util/merge');
-
-var _merge2 = _interopRequireDefault(_merge);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-var default_normalize_email_options = {
- // The following options apply to all email addresses
- // Lowercases the local part of the email address.
- // Please note this may violate RFC 5321 as per http://stackoverflow.com/a/9808332/192024).
- // The domain is always lowercased, as per RFC 1035
- all_lowercase: true,
-
- // The following conversions are specific to GMail
- // Lowercases the local part of the GMail address (known to be case-insensitive)
- gmail_lowercase: true,
- // Removes dots from the local part of the email address, as that's ignored by GMail
- gmail_remove_dots: true,
- // Removes the subaddress (e.g. "+foo") from the email address
- gmail_remove_subaddress: true,
- // Conversts the googlemail.com domain to gmail.com
- gmail_convert_googlemaildotcom: true,
-
- // The following conversions are specific to Outlook.com / Windows Live / Hotmail
- // Lowercases the local part of the Outlook.com address (known to be case-insensitive)
- outlookdotcom_lowercase: true,
- // Removes the subaddress (e.g. "+foo") from the email address
- outlookdotcom_remove_subaddress: true,
-
- // The following conversions are specific to Yahoo
- // Lowercases the local part of the Yahoo address (known to be case-insensitive)
- yahoo_lowercase: true,
- // Removes the subaddress (e.g. "-foo") from the email address
- yahoo_remove_subaddress: true,
-
- // The following conversions are specific to iCloud
- // Lowercases the local part of the iCloud address (known to be case-insensitive)
- icloud_lowercase: true,
- // Removes the subaddress (e.g. "+foo") from the email address
- icloud_remove_subaddress: true
-};
-
-// List of domains used by iCloud
-var icloud_domains = ['icloud.com', 'me.com'];
-
-// List of domains used by Outlook.com and its predecessors
-// This list is likely incomplete.
-// Partial reference:
-// https://blogs.office.com/2013/04/17/outlook-com-gets-two-step-verification-sign-in-by-alias-and-new-international-domains/
-var outlookdotcom_domains = ['hotmail.at', 'hotmail.be', 'hotmail.ca', 'hotmail.cl', 'hotmail.co.il', 'hotmail.co.nz', 'hotmail.co.th', 'hotmail.co.uk', 'hotmail.com', 'hotmail.com.ar', 'hotmail.com.au', 'hotmail.com.br', 'hotmail.com.gr', 'hotmail.com.mx', 'hotmail.com.pe', 'hotmail.com.tr', 'hotmail.com.vn', 'hotmail.cz', 'hotmail.de', 'hotmail.dk', 'hotmail.es', 'hotmail.fr', 'hotmail.hu', 'hotmail.id', 'hotmail.ie', 'hotmail.in', 'hotmail.it', 'hotmail.jp', 'hotmail.kr', 'hotmail.lv', 'hotmail.my', 'hotmail.ph', 'hotmail.pt', 'hotmail.sa', 'hotmail.sg', 'hotmail.sk', 'live.be', 'live.co.uk', 'live.com', 'live.com.ar', 'live.com.mx', 'live.de', 'live.es', 'live.eu', 'live.fr', 'live.it', 'live.nl', 'msn.com', 'outlook.at', 'outlook.be', 'outlook.cl', 'outlook.co.il', 'outlook.co.nz', 'outlook.co.th', 'outlook.com', 'outlook.com.ar', 'outlook.com.au', 'outlook.com.br', 'outlook.com.gr', 'outlook.com.pe', 'outlook.com.tr', 'outlook.com.vn', 'outlook.cz', 'outlook.de', 'outlook.dk', 'outlook.es', 'outlook.fr', 'outlook.hu', 'outlook.id', 'outlook.ie', 'outlook.in', 'outlook.it', 'outlook.jp', 'outlook.kr', 'outlook.lv', 'outlook.my', 'outlook.ph', 'outlook.pt', 'outlook.sa', 'outlook.sg', 'outlook.sk', 'passport.com'];
-
-// List of domains used by Yahoo Mail
-// This list is likely incomplete
-var yahoo_domains = ['rocketmail.com', 'yahoo.ca', 'yahoo.co.uk', 'yahoo.com', 'yahoo.de', 'yahoo.fr', 'yahoo.in', 'yahoo.it', 'ymail.com'];
-
-function normalizeEmail(email, options) {
- options = (0, _merge2.default)(options, default_normalize_email_options);
-
- var raw_parts = email.split('@');
- var domain = raw_parts.pop();
- var user = raw_parts.join('@');
- var parts = [user, domain];
-
- // The domain is always lowercased, as it's case-insensitive per RFC 1035
- parts[1] = parts[1].toLowerCase();
-
- if (parts[1] === 'gmail.com' || parts[1] === 'googlemail.com') {
- // Address is GMail
- if (options.gmail_remove_subaddress) {
- parts[0] = parts[0].split('+')[0];
- }
- if (options.gmail_remove_dots) {
- parts[0] = parts[0].replace(/\./g, '');
- }
- if (!parts[0].length) {
- return false;
- }
- if (options.all_lowercase || options.gmail_lowercase) {
- parts[0] = parts[0].toLowerCase();
- }
- parts[1] = options.gmail_convert_googlemaildotcom ? 'gmail.com' : parts[1];
- } else if (~icloud_domains.indexOf(parts[1])) {
- // Address is iCloud
- if (options.icloud_remove_subaddress) {
- parts[0] = parts[0].split('+')[0];
- }
- if (!parts[0].length) {
- return false;
- }
- if (options.all_lowercase || options.icloud_lowercase) {
- parts[0] = parts[0].toLowerCase();
- }
- } else if (~outlookdotcom_domains.indexOf(parts[1])) {
- // Address is Outlook.com
- if (options.outlookdotcom_remove_subaddress) {
- parts[0] = parts[0].split('+')[0];
- }
- if (!parts[0].length) {
- return false;
- }
- if (options.all_lowercase || options.outlookdotcom_lowercase) {
- parts[0] = parts[0].toLowerCase();
- }
- } else if (~yahoo_domains.indexOf(parts[1])) {
- // Address is Yahoo
- if (options.yahoo_remove_subaddress) {
- var components = parts[0].split('-');
- parts[0] = components.length > 1 ? components.slice(0, -1).join('-') : components[0];
- }
- if (!parts[0].length) {
- return false;
- }
- if (options.all_lowercase || options.yahoo_lowercase) {
- parts[0] = parts[0].toLowerCase();
- }
- } else if (options.all_lowercase) {
- // Any other address
- parts[0] = parts[0].toLowerCase();
- }
- return parts.join('@');
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/rtrim.js b/lib/rtrim.js
deleted file mode 100644
index fa6d8e15d..000000000
--- a/lib/rtrim.js
+++ /dev/null
@@ -1,25 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = rtrim;
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-function rtrim(str, chars) {
- (0, _assertString2.default)(str);
- var pattern = chars ? new RegExp('[' + chars + ']') : /\s/;
-
- var idx = str.length - 1;
- while (idx >= 0 && pattern.test(str[idx])) {
- idx--;
- }
-
- return idx < str.length ? str.substr(0, idx + 1) : str;
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/stripLow.js b/lib/stripLow.js
deleted file mode 100644
index bdf395fef..000000000
--- a/lib/stripLow.js
+++ /dev/null
@@ -1,23 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = stripLow;
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-var _blacklist = require('./blacklist');
-
-var _blacklist2 = _interopRequireDefault(_blacklist);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-function stripLow(str, keep_new_lines) {
- (0, _assertString2.default)(str);
- var chars = keep_new_lines ? '\\x00-\\x09\\x0B\\x0C\\x0E-\\x1F\\x7F' : '\\x00-\\x1F\\x7F';
- return (0, _blacklist2.default)(str, chars);
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/toBoolean.js b/lib/toBoolean.js
deleted file mode 100644
index 8ac35e358..000000000
--- a/lib/toBoolean.js
+++ /dev/null
@@ -1,21 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = toBoolean;
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-function toBoolean(str, strict) {
- (0, _assertString2.default)(str);
- if (strict) {
- return str === '1' || str === 'true';
- }
- return str !== '0' && str !== 'false' && str !== '';
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/toDate.js b/lib/toDate.js
deleted file mode 100644
index 80e21f386..000000000
--- a/lib/toDate.js
+++ /dev/null
@@ -1,19 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = toDate;
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-function toDate(date) {
- (0, _assertString2.default)(date);
- date = Date.parse(date);
- return !isNaN(date) ? new Date(date) : null;
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/toFloat.js b/lib/toFloat.js
deleted file mode 100644
index 3a8e256bb..000000000
--- a/lib/toFloat.js
+++ /dev/null
@@ -1,18 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = toFloat;
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-function toFloat(str) {
- (0, _assertString2.default)(str);
- return parseFloat(str);
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/toInt.js b/lib/toInt.js
deleted file mode 100644
index 97862a41f..000000000
--- a/lib/toInt.js
+++ /dev/null
@@ -1,18 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = toInt;
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-function toInt(str, radix) {
- (0, _assertString2.default)(str);
- return parseInt(str, radix || 10);
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/trim.js b/lib/trim.js
deleted file mode 100644
index cb945d450..000000000
--- a/lib/trim.js
+++ /dev/null
@@ -1,21 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = trim;
-
-var _rtrim = require('./rtrim');
-
-var _rtrim2 = _interopRequireDefault(_rtrim);
-
-var _ltrim = require('./ltrim');
-
-var _ltrim2 = _interopRequireDefault(_ltrim);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-function trim(str, chars) {
- return (0, _rtrim2.default)((0, _ltrim2.default)(str, chars), chars);
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/unescape.js b/lib/unescape.js
deleted file mode 100644
index cdcd7c96c..000000000
--- a/lib/unescape.js
+++ /dev/null
@@ -1,18 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = unescape;
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-function unescape(str) {
- (0, _assertString2.default)(str);
- return str.replace(/&/g, '&').replace(/"/g, '"').replace(/'/g, "'").replace(/</g, '<').replace(/>/g, '>').replace(///g, '/').replace(/\/g, '\\').replace(/`/g, '`');
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/util/assertString.js b/lib/util/assertString.js
deleted file mode 100644
index 4abc1228f..000000000
--- a/lib/util/assertString.js
+++ /dev/null
@@ -1,14 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = assertString;
-function assertString(input) {
- var isString = typeof input === 'string' || input instanceof String;
-
- if (!isString) {
- throw new TypeError('This library (validator.js) validates strings only');
- }
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/util/merge.js b/lib/util/merge.js
deleted file mode 100644
index 68d66fbb8..000000000
--- a/lib/util/merge.js
+++ /dev/null
@@ -1,18 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = merge;
-function merge() {
- var obj = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
- var defaults = arguments[1];
-
- for (var key in defaults) {
- if (typeof obj[key] === 'undefined') {
- obj[key] = defaults[key];
- }
- }
- return obj;
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/util/toString.js b/lib/util/toString.js
deleted file mode 100644
index e5d5c0db1..000000000
--- a/lib/util/toString.js
+++ /dev/null
@@ -1,22 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-
-var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
-
-exports.default = toString;
-function toString(input) {
- if ((typeof input === 'undefined' ? 'undefined' : _typeof(input)) === 'object' && input !== null) {
- if (typeof input.toString === 'function') {
- input = input.toString();
- } else {
- input = '[object Object]';
- }
- } else if (input === null || typeof input === 'undefined' || isNaN(input) && !input.length) {
- input = '';
- }
- return String(input);
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/lib/whitelist.js b/lib/whitelist.js
deleted file mode 100644
index 7a06c5d88..000000000
--- a/lib/whitelist.js
+++ /dev/null
@@ -1,18 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = whitelist;
-
-var _assertString = require('./util/assertString');
-
-var _assertString2 = _interopRequireDefault(_assertString);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-function whitelist(str, chars) {
- (0, _assertString2.default)(str);
- return str.replace(new RegExp('[^' + chars + ']+', 'g'), '');
-}
-module.exports = exports['default'];
\ No newline at end of file
diff --git a/package.json b/package.json
index 61506e924..106694955 100644
--- a/package.json
+++ b/package.json
@@ -1,13 +1,15 @@
{
"name": "validator",
"description": "String validation and sanitization",
- "version": "9.1.2",
- "homepage": "http://github.com/chriso/validator.js",
+ "version": "13.15.20",
+ "sideEffects": false,
+ "homepage": "https://github.com/validatorjs/validator.js",
"files": [
"index.js",
+ "es",
"lib",
"README.md",
- "LICENCE",
+ "LICENSE",
"validator.js",
"validator.min.js"
],
@@ -22,39 +24,50 @@
"assert"
],
"author": "Chris O'Hara ",
+ "contributors": [
+ "Anthony Nandaa (https://github.com/profnandaa)"
+ ],
"main": "index.js",
"bugs": {
- "url": "http://github.com/chriso/validator.js/issues"
+ "url": "https://github.com/validatorjs/validator.js/issues"
},
"repository": {
"type": "git",
- "url": "http://github.com/chriso/validator.js.git"
+ "url": "git+https://github.com/validatorjs/validator.js.git"
},
"devDependencies": {
- "babel-cli": "^6.24.0",
- "babel-plugin-add-module-exports": "^0.2.1",
- "babel-preset-es2015": "^6.24.0",
- "babel-preset-es2015-rollup": "^3.0.0",
- "eslint": "^4.0.0",
- "eslint-config-airbnb-base": "^11.2.0",
- "eslint-plugin-import": "^2.3.0",
- "mocha": "^3.1.2",
- "rollup": "^0.43.0",
- "rollup-plugin-babel": "^2.7.1",
+ "@babel/cli": "^7.0.0",
+ "@babel/core": "^7.0.0",
+ "@babel/preset-env": "^7.0.0",
+ "@babel/register": "^7.0.0",
+ "babel-eslint": "^10.0.1",
+ "babel-plugin-add-module-exports": "^1.0.0",
+ "eslint": "^4.19.1",
+ "eslint-config-airbnb-base": "^12.1.0",
+ "eslint-plugin-import": "^2.11.0",
+ "mocha": "^6.2.3",
+ "npm-run-all": "^4.1.5",
+ "nyc": "^14.1.0",
+ "rimraf": "^3.0.0",
+ "rollup": "^0.47.0",
+ "rollup-plugin-babel": "^4.0.1",
+ "timezone-mock": "^1.3.6",
"uglify-js": "^3.0.19"
},
"scripts": {
"lint": "eslint src test",
"lint:fix": "eslint --fix src test",
- "clean:node": "rm -rf index.js lib",
- "clean:browser": "rm -rf validator*.js",
- "clean": "npm run clean:node && npm run clean:browser",
+ "clean:node": "rimraf index.js lib",
+ "clean:es": "rimraf es",
+ "clean:browser": "rimraf validator*.js",
+ "clean": "run-p clean:*",
"minify": "uglifyjs validator.js -o validator.min.js --compress --mangle --comments /Copyright/",
- "build:browser": "babel-node build-browser && npm run minify",
- "build:node": "babel src -d . --presets es2015 --plugins add-module-exports",
- "build": "npm run build:browser && npm run build:node",
- "pretest": "npm run lint && npm run build",
- "test": "mocha --reporter spec"
+ "build:browser": "node --require @babel/register build-browser && npm run minify",
+ "build:es": "babel src -d es --env-name=es",
+ "build:node": "babel src -d .",
+ "build": "run-p build:*",
+ "pretest": "npm run build && npm run lint",
+ "test": "nyc --reporter=cobertura --reporter=text-summary mocha --require @babel/register --reporter dot --recursive"
},
"engines": {
"node": ">= 0.10"
diff --git a/src/.eslintrc.json b/src/.eslintrc.json
deleted file mode 100644
index 1e4ea7e6b..000000000
--- a/src/.eslintrc.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "rules": {
- "no-console": 1,
- "no-plusplus": 0,
- "no-bitwise": 0,
- "no-prototype-builtins": 0,
- "no-useless-escape": 0
- }
-}
diff --git a/src/index.js b/src/index.js
index 24996e071..b69c43649 100644
--- a/src/index.js
+++ b/src/index.js
@@ -10,36 +10,52 @@ import isEmail from './lib/isEmail';
import isURL from './lib/isURL';
import isMACAddress from './lib/isMACAddress';
import isIP from './lib/isIP';
+import isIPRange from './lib/isIPRange';
import isFQDN from './lib/isFQDN';
+import isDate from './lib/isDate';
+import isTime from './lib/isTime';
import isBoolean from './lib/isBoolean';
+import isLocale from './lib/isLocale';
-import isAlpha from './lib/isAlpha';
-import isAlphanumeric from './lib/isAlphanumeric';
+import isAbaRouting from './lib/isAbaRouting';
+import isAlpha, { locales as isAlphaLocales } from './lib/isAlpha';
+import isAlphanumeric, { locales as isAlphanumericLocales } from './lib/isAlphanumeric';
import isNumeric from './lib/isNumeric';
+import isPassportNumber, { locales as passportNumberLocales } from './lib/isPassportNumber';
import isPort from './lib/isPort';
import isLowercase from './lib/isLowercase';
import isUppercase from './lib/isUppercase';
+import isIMEI from './lib/isIMEI';
+
import isAscii from './lib/isAscii';
import isFullWidth from './lib/isFullWidth';
import isHalfWidth from './lib/isHalfWidth';
import isVariableWidth from './lib/isVariableWidth';
import isMultibyte from './lib/isMultibyte';
+import isSemVer from './lib/isSemVer';
import isSurrogatePair from './lib/isSurrogatePair';
import isInt from './lib/isInt';
-import isFloat from './lib/isFloat';
+import isFloat, { locales as isFloatLocales } from './lib/isFloat';
import isDecimal from './lib/isDecimal';
import isHexadecimal from './lib/isHexadecimal';
+import isOctal from './lib/isOctal';
import isDivisibleBy from './lib/isDivisibleBy';
import isHexColor from './lib/isHexColor';
+import isRgbColor from './lib/isRgbColor';
+import isHSL from './lib/isHSL';
import isISRC from './lib/isISRC';
+import isIBAN, { locales as ibanLocales } from './lib/isIBAN';
+import isBIC from './lib/isBIC';
+
import isMD5 from './lib/isMD5';
import isHash from './lib/isHash';
+import isJWT from './lib/isJWT';
import isJSON from './lib/isJSON';
import isEmpty from './lib/isEmpty';
@@ -47,6 +63,7 @@ import isEmpty from './lib/isEmpty';
import isLength from './lib/isLength';
import isByteLength from './lib/isByteLength';
+import isULID from './lib/isULID';
import isUUID from './lib/isUUID';
import isMongoId from './lib/isMongoId';
@@ -55,24 +72,45 @@ import isBefore from './lib/isBefore';
import isIn from './lib/isIn';
+import isLuhnNumber from './lib/isLuhnNumber';
import isCreditCard from './lib/isCreditCard';
+import isIdentityCard from './lib/isIdentityCard';
+import isEAN from './lib/isEAN';
import isISIN from './lib/isISIN';
import isISBN from './lib/isISBN';
import isISSN from './lib/isISSN';
+import isTaxID from './lib/isTaxID';
+
+import isMobilePhone, { locales as isMobilePhoneLocales } from './lib/isMobilePhone';
-import isMobilePhone from './lib/isMobilePhone';
+import isEthereumAddress from './lib/isEthereumAddress';
import isCurrency from './lib/isCurrency';
+import isBtcAddress from './lib/isBtcAddress';
+
+import { isISO6346, isFreightContainerID } from './lib/isISO6346';
+import isISO6391 from './lib/isISO6391';
import isISO8601 from './lib/isISO8601';
+import isRFC3339 from './lib/isRFC3339';
+import isISO15924 from './lib/isISO15924';
import isISO31661Alpha2 from './lib/isISO31661Alpha2';
+import isISO31661Alpha3 from './lib/isISO31661Alpha3';
+import isISO31661Numeric from './lib/isISO31661Numeric';
+import isISO4217 from './lib/isISO4217';
+import isBase32 from './lib/isBase32';
+import isBase58 from './lib/isBase58';
import isBase64 from './lib/isBase64';
import isDataURI from './lib/isDataURI';
+import isMagnetURI from './lib/isMagnetURI';
+import isMailtoURI from './lib/isMailtoURI';
+
+import isMimeType from './lib/isMimeType';
import isLatLong from './lib/isLatLong';
-import isPostalCode from './lib/isPostalCode';
+import isPostalCode, { locales as isPostalCodeLocales } from './lib/isPostalCode';
import ltrim from './lib/ltrim';
import rtrim from './lib/rtrim';
@@ -86,9 +124,13 @@ import isWhitelisted from './lib/isWhitelisted';
import normalizeEmail from './lib/normalizeEmail';
-import toString from './lib/util/toString';
+import isSlug from './lib/isSlug';
+import isLicensePlate from './lib/isLicensePlate';
+import isStrongPassword from './lib/isStrongPassword';
+
+import isVAT from './lib/isVAT';
-const version = '9.1.2';
+const version = '13.15.20';
const validator = {
version,
@@ -103,11 +145,19 @@ const validator = {
isURL,
isMACAddress,
isIP,
+ isIPRange,
isFQDN,
isBoolean,
+ isIBAN,
+ isBIC,
+ isAbaRouting,
isAlpha,
+ isAlphaLocales,
isAlphanumeric,
+ isAlphanumericLocales,
isNumeric,
+ isPassportNumber,
+ passportNumberLocales,
isPort,
isLowercase,
isUppercase,
@@ -116,36 +166,65 @@ const validator = {
isHalfWidth,
isVariableWidth,
isMultibyte,
+ isSemVer,
isSurrogatePair,
isInt,
+ isIMEI,
isFloat,
+ isFloatLocales,
isDecimal,
isHexadecimal,
+ isOctal,
isDivisibleBy,
isHexColor,
+ isRgbColor,
+ isHSL,
isISRC,
isMD5,
isHash,
+ isJWT,
isJSON,
isEmpty,
isLength,
+ isLocale,
isByteLength,
+ isULID,
isUUID,
isMongoId,
isAfter,
isBefore,
isIn,
+ isLuhnNumber,
isCreditCard,
+ isIdentityCard,
+ isEAN,
isISIN,
isISBN,
isISSN,
isMobilePhone,
+ isMobilePhoneLocales,
isPostalCode,
+ isPostalCodeLocales,
+ isEthereumAddress,
isCurrency,
+ isBtcAddress,
+ isISO6346,
+ isFreightContainerID,
+ isISO6391,
isISO8601,
+ isISO15924,
+ isRFC3339,
isISO31661Alpha2,
+ isISO31661Alpha3,
+ isISO31661Numeric,
+ isISO4217,
+ isBase32,
+ isBase58,
isBase64,
isDataURI,
+ isMagnetURI,
+ isMailtoURI,
+ isMimeType,
isLatLong,
ltrim,
rtrim,
@@ -158,6 +237,14 @@ const validator = {
isWhitelisted,
normalizeEmail,
toString,
+ isSlug,
+ isStrongPassword,
+ isTaxID,
+ isDate,
+ isTime,
+ isLicensePlate,
+ isVAT,
+ ibanLocales,
};
export default validator;
diff --git a/src/lib/alpha.js b/src/lib/alpha.js
index ccc13e650..6f58c9aee 100644
--- a/src/lib/alpha.js
+++ b/src/lib/alpha.js
@@ -1,47 +1,98 @@
export const alpha = {
'en-US': /^[A-Z]+$/i,
+ 'az-AZ': /^[A-VXYZÇƏĞİıÖŞÜ]+$/i,
+ 'bg-BG': /^[А-Я]+$/i,
'cs-CZ': /^[A-ZÁČĎÉĚÍŇÓŘŠŤÚŮÝŽ]+$/i,
'da-DK': /^[A-ZÆØÅ]+$/i,
'de-DE': /^[A-ZÄÖÜß]+$/i,
+ 'el-GR': /^[Α-ώ]+$/i,
'es-ES': /^[A-ZÁÉÍÑÓÚÜ]+$/i,
+ 'fa-IR': /^[ابپتثجچحخدذرزژسشصضطظعغفقکگلمنوهی]+$/i,
+ 'fi-FI': /^[A-ZÅÄÖ]+$/i,
'fr-FR': /^[A-ZÀÂÆÇÉÈÊËÏÎÔŒÙÛÜŸ]+$/i,
'it-IT': /^[A-ZÀÉÈÌÎÓÒÙ]+$/i,
+ 'ja-JP': /^[ぁ-んァ-ヶヲ-゚一-龠ー・。、]+$/i,
'nb-NO': /^[A-ZÆØÅ]+$/i,
'nl-NL': /^[A-ZÁÉËÏÓÖÜÚ]+$/i,
'nn-NO': /^[A-ZÆØÅ]+$/i,
'hu-HU': /^[A-ZÁÉÍÓÖŐÚÜŰ]+$/i,
'pl-PL': /^[A-ZĄĆĘŚŁŃÓŻŹ]+$/i,
- 'pt-PT': /^[A-ZÃÁÀÂÇÉÊÍÕÓÔÚÜ]+$/i,
+ 'pt-PT': /^[A-ZÃÁÀÂÄÇÉÊËÍÏÕÓÔÖÚÜ]+$/i,
'ru-RU': /^[А-ЯЁ]+$/i,
+ 'kk-KZ': /^[А-ЯЁ\u04D8\u04B0\u0406\u04A2\u0492\u04AE\u049A\u04E8\u04BA]+$/i,
+ 'sl-SI': /^[A-ZČĆĐŠŽ]+$/i,
+ 'sk-SK': /^[A-ZÁČĎÉÍŇÓŠŤÚÝŽĹŔĽÄÔ]+$/i,
'sr-RS@latin': /^[A-ZČĆŽŠĐ]+$/i,
'sr-RS': /^[А-ЯЂЈЉЊЋЏ]+$/i,
'sv-SE': /^[A-ZÅÄÖ]+$/i,
+ 'th-TH': /^[ก-๐\s]+$/i,
'tr-TR': /^[A-ZÇĞİıÖŞÜ]+$/i,
'uk-UA': /^[А-ЩЬЮЯЄIЇҐі]+$/i,
+ 'vi-VN': /^[A-ZÀÁẠẢÃÂẦẤẬẨẪĂẰẮẶẲẴĐÈÉẸẺẼÊỀẾỆỂỄÌÍỊỈĨÒÓỌỎÕÔỒỐỘỔỖƠỜỚỢỞỠÙÚỤỦŨƯỪỨỰỬỮỲÝỴỶỸ]+$/i,
+ 'ko-KR': /^[ㄱ-ㅎㅏ-ㅣ가-힣]*$/,
+ 'ku-IQ': /^[ئابپتجچحخدرڕزژسشعغفڤقکگلڵمنوۆھەیێيطؤثآإأكضصةظذ]+$/i,
ar: /^[ءآأؤإئابةتثجحخدذرزسشصضطظعغفقكلمنهوىيًٌٍَُِّْٰ]+$/,
+ he: /^[א-ת]+$/,
+ fa: /^['آاءأؤئبپتثجچحخدذرزژسشصضطظعغفقکگلمنوهةی']+$/i,
+ bn: /^['ঀঁংঃঅআইঈউঊঋঌএঐওঔকখগঘঙচছজঝঞটঠডঢণতথদধনপফবভমযরলশষসহ়ঽািীুূৃৄেৈোৌ্ৎৗড়ঢ়য়ৠৡৢৣৰৱ৲৳৴৵৶৷৸৹৺৻']+$/,
+ eo: /^[ABCĈD-GĜHĤIJĴK-PRSŜTUŬVZ]+$/i,
+ 'hi-IN': /^[\u0900-\u0961]+[\u0972-\u097F]*$/i,
+ 'si-LK': /^[\u0D80-\u0DFF]+$/,
+ 'ta-IN': /^[\u0B80-\u0BFF]+$/i,
+ 'te-IN': /^[\u0C00-\u0C7F]+$/i,
+ 'kn-IN': /^[\u0C80-\u0CFF]+$/i,
+ 'ml-IN': /^[\u0D00-\u0D7F]+$/i,
+ 'gu-IN': /^[\u0A80-\u0AFF]+$/i,
+ 'pa-IN': /^[\u0A00-\u0A7F]+$/i,
+ 'or-IN': /^[\u0B00-\u0B7F]+$/i,
};
export const alphanumeric = {
'en-US': /^[0-9A-Z]+$/i,
+ 'az-AZ': /^[0-9A-VXYZÇƏĞİıÖŞÜ]+$/i,
+ 'bg-BG': /^[0-9А-Я]+$/i,
'cs-CZ': /^[0-9A-ZÁČĎÉĚÍŇÓŘŠŤÚŮÝŽ]+$/i,
'da-DK': /^[0-9A-ZÆØÅ]+$/i,
'de-DE': /^[0-9A-ZÄÖÜß]+$/i,
+ 'el-GR': /^[0-9Α-ω]+$/i,
'es-ES': /^[0-9A-ZÁÉÍÑÓÚÜ]+$/i,
+ 'fi-FI': /^[0-9A-ZÅÄÖ]+$/i,
'fr-FR': /^[0-9A-ZÀÂÆÇÉÈÊËÏÎÔŒÙÛÜŸ]+$/i,
'it-IT': /^[0-9A-ZÀÉÈÌÎÓÒÙ]+$/i,
+ 'ja-JP': /^[0-90-9ぁ-んァ-ヶヲ-゚一-龠ー・。、]+$/i,
'hu-HU': /^[0-9A-ZÁÉÍÓÖŐÚÜŰ]+$/i,
'nb-NO': /^[0-9A-ZÆØÅ]+$/i,
'nl-NL': /^[0-9A-ZÁÉËÏÓÖÜÚ]+$/i,
'nn-NO': /^[0-9A-ZÆØÅ]+$/i,
'pl-PL': /^[0-9A-ZĄĆĘŚŁŃÓŻŹ]+$/i,
- 'pt-PT': /^[0-9A-ZÃÁÀÂÇÉÊÍÕÓÔÚÜ]+$/i,
+ 'pt-PT': /^[0-9A-ZÃÁÀÂÄÇÉÊËÍÏÕÓÔÖÚÜ]+$/i,
'ru-RU': /^[0-9А-ЯЁ]+$/i,
+ 'kk-KZ': /^[0-9А-ЯЁ\u04D8\u04B0\u0406\u04A2\u0492\u04AE\u049A\u04E8\u04BA]+$/i,
+ 'sl-SI': /^[0-9A-ZČĆĐŠŽ]+$/i,
+ 'sk-SK': /^[0-9A-ZÁČĎÉÍŇÓŠŤÚÝŽĹŔĽÄÔ]+$/i,
'sr-RS@latin': /^[0-9A-ZČĆŽŠĐ]+$/i,
'sr-RS': /^[0-9А-ЯЂЈЉЊЋЏ]+$/i,
'sv-SE': /^[0-9A-ZÅÄÖ]+$/i,
+ 'th-TH': /^[ก-๙\s]+$/i,
'tr-TR': /^[0-9A-ZÇĞİıÖŞÜ]+$/i,
'uk-UA': /^[0-9А-ЩЬЮЯЄIЇҐі]+$/i,
+ 'ko-KR': /^[0-9ㄱ-ㅎㅏ-ㅣ가-힣]*$/,
+ 'ku-IQ': /^[٠١٢٣٤٥٦٧٨٩0-9ئابپتجچحخدرڕزژسشعغفڤقکگلڵمنوۆھەیێيطؤثآإأكضصةظذ]+$/i,
+ 'vi-VN': /^[0-9A-ZÀÁẠẢÃÂẦẤẬẨẪĂẰẮẶẲẴĐÈÉẸẺẼÊỀẾỆỂỄÌÍỊỈĨÒÓỌỎÕÔỒỐỘỔỖƠỜỚỢỞỠÙÚỤỦŨƯỪỨỰỬỮỲÝỴỶỸ]+$/i,
ar: /^[٠١٢٣٤٥٦٧٨٩0-9ءآأؤإئابةتثجحخدذرزسشصضطظعغفقكلمنهوىيًٌٍَُِّْٰ]+$/,
+ he: /^[0-9א-ת]+$/,
+ fa: /^['0-9آاءأؤئبپتثجچحخدذرزژسشصضطظعغفقکگلمنوهةی۱۲۳۴۵۶۷۸۹۰']+$/i,
+ bn: /^['ঀঁংঃঅআইঈউঊঋঌএঐওঔকখগঘঙচছজঝঞটঠডঢণতথদধনপফবভমযরলশষসহ়ঽািীুূৃৄেৈোৌ্ৎৗড়ঢ়য়ৠৡৢৣ০১২৩৪৫৬৭৮৯ৰৱ৲৳৴৵৶৷৸৹৺৻']+$/,
+ eo: /^[0-9ABCĈD-GĜHĤIJĴK-PRSŜTUŬVZ]+$/i,
+ 'hi-IN': /^[\u0900-\u0963]+[\u0966-\u097F]*$/i,
+ 'si-LK': /^[0-9\u0D80-\u0DFF]+$/,
+ 'ta-IN': /^[0-9\u0B80-\u0BFF.]+$/i,
+ 'te-IN': /^[0-9\u0C00-\u0C7F.]+$/i,
+ 'kn-IN': /^[0-9\u0C80-\u0CFF.]+$/i,
+ 'ml-IN': /^[0-9\u0D00-\u0D7F.]+$/i,
+ 'gu-IN': /^[0-9\u0A80-\u0AFF.]+$/i,
+ 'pa-IN': /^[0-9\u0A00-\u0A7F.]+$/i,
+ 'or-IN': /^[0-9\u0B00-\u0B7F.]+$/i,
};
export const decimal = {
@@ -60,10 +111,8 @@ for (let locale, i = 0; i < englishLocales.length; i++) {
}
// Source: http://www.localeplanet.com/java/
-export const arabicLocales = [
- 'AE', 'BH', 'DZ', 'EG', 'IQ', 'JO', 'KW', 'LB', 'LY',
- 'MA', 'QM', 'QA', 'SA', 'SD', 'SY', 'TN', 'YE',
-];
+export const arabicLocales = ['AE', 'BH', 'DZ', 'EG', 'IQ', 'JO', 'KW', 'LB', 'LY',
+ 'MA', 'QM', 'QA', 'SA', 'SD', 'SY', 'TN', 'YE'];
for (let locale, i = 0; i < arabicLocales.length; i++) {
locale = `ar-${arabicLocales[i]}`;
@@ -72,12 +121,30 @@ for (let locale, i = 0; i < arabicLocales.length; i++) {
decimal[locale] = decimal.ar;
}
+export const farsiLocales = ['IR', 'AF'];
+
+for (let locale, i = 0; i < farsiLocales.length; i++) {
+ locale = `fa-${farsiLocales[i]}`;
+ alphanumeric[locale] = alphanumeric.fa;
+ decimal[locale] = decimal.ar;
+}
+
+export const bengaliLocales = ['BD', 'IN'];
+
+for (let locale, i = 0; i < bengaliLocales.length; i++) {
+ locale = `bn-${bengaliLocales[i]}`;
+ alpha[locale] = alpha.bn;
+ alphanumeric[locale] = alphanumeric.bn;
+ decimal[locale] = decimal['en-US'];
+}
+
// Source: https://en.wikipedia.org/wiki/Decimal_mark
-export const dotDecimal = [];
+export const dotDecimal = ['ar-EG', 'ar-LB', 'ar-LY'];
export const commaDecimal = [
- 'cs-CZ', 'da-DK', 'de-DE', 'es-ES', 'fr-FR', 'it-IT', 'hu-HU', 'nb-NO',
- 'nn-NO', 'nl-NL', 'pl-Pl', 'pt-PT', 'ru-RU', 'sr-RS@latin',
- 'sr-RS', 'sv-SE', 'tr-TR', 'uk-UA',
+ 'bg-BG', 'cs-CZ', 'da-DK', 'de-DE', 'el-GR', 'en-ZM', 'eo', 'es-ES', 'fr-CA', 'fr-FR',
+ 'gu-IN', 'hi-IN', 'hu-HU', 'id-ID', 'it-IT', 'kk-KZ', 'kn-IN', 'ku-IQ', 'ml-IN', 'nb-NO',
+ 'nl-NL', 'nn-NO', 'or-IN', 'pa-IN', 'pl-PL', 'pt-PT', 'ru-RU', 'si-LK', 'sl-SI', 'sr-RS',
+ 'sr-RS@latin', 'sv-SE', 'ta-IN', 'te-IN', 'tr-TR', 'uk-UA', 'vi-VN',
];
for (let i = 0; i < dotDecimal.length; i++) {
@@ -88,6 +155,17 @@ for (let i = 0; i < commaDecimal.length; i++) {
decimal[commaDecimal[i]] = ',';
}
+alpha['fr-CA'] = alpha['fr-FR'];
+alphanumeric['fr-CA'] = alphanumeric['fr-FR'];
+
alpha['pt-BR'] = alpha['pt-PT'];
alphanumeric['pt-BR'] = alphanumeric['pt-PT'];
decimal['pt-BR'] = decimal['pt-PT'];
+
+// see #862
+alpha['pl-Pl'] = alpha['pl-PL'];
+alphanumeric['pl-Pl'] = alphanumeric['pl-PL'];
+decimal['pl-Pl'] = decimal['pl-PL'];
+
+// see #1455
+alpha['fa-AF'] = alpha.fa;
diff --git a/src/lib/contains.js b/src/lib/contains.js
index fd8c578de..8e716c4be 100644
--- a/src/lib/contains.js
+++ b/src/lib/contains.js
@@ -1,7 +1,19 @@
import assertString from './util/assertString';
import toString from './util/toString';
+import merge from './util/merge';
-export default function contains(str, elem) {
+const defaultContainsOptions = {
+ ignoreCase: false,
+ minOccurrences: 1,
+};
+
+export default function contains(str, elem, options) {
assertString(str);
- return str.indexOf(toString(elem)) >= 0;
+ options = merge(options, defaultContainsOptions);
+
+ if (options.ignoreCase) {
+ return str.toLowerCase().split(toString(elem).toLowerCase()).length > options.minOccurrences;
+ }
+
+ return str.split(toString(elem)).length > options.minOccurrences;
}
diff --git a/src/lib/isAbaRouting.js b/src/lib/isAbaRouting.js
new file mode 100644
index 000000000..0c6fd7fb2
--- /dev/null
+++ b/src/lib/isAbaRouting.js
@@ -0,0 +1,20 @@
+import assertString from './util/assertString';
+
+// http://www.brainjar.com/js/validation/
+// https://www.aba.com/news-research/research-analysis/routing-number-policy-procedures
+// series reserved for future use are excluded
+const isRoutingReg = /^(?!(1[3-9])|(20)|(3[3-9])|(4[0-9])|(5[0-9])|(60)|(7[3-9])|(8[1-9])|(9[0-2])|(9[3-9]))[0-9]{9}$/;
+
+export default function isAbaRouting(str) {
+ assertString(str);
+
+ if (!isRoutingReg.test(str)) return false;
+
+ let checkSumVal = 0;
+ for (let i = 0; i < str.length; i++) {
+ if (i % 3 === 0) checkSumVal += str[i] * 3;
+ else if (i % 3 === 1) checkSumVal += str[i] * 7;
+ else checkSumVal += str[i] * 1;
+ }
+ return (checkSumVal % 10 === 0);
+}
diff --git a/src/lib/isAfter.js b/src/lib/isAfter.js
index 47bfb537f..149622a0d 100644
--- a/src/lib/isAfter.js
+++ b/src/lib/isAfter.js
@@ -1,9 +1,12 @@
-import assertString from './util/assertString';
import toDate from './toDate';
-export default function isAfter(str, date = String(new Date())) {
- assertString(str);
- const comparison = toDate(date);
- const original = toDate(str);
+export default function isAfter(date, options) {
+ // For backwards compatibility:
+ // isAfter(str [, date]), i.e. `options` could be used as argument for the legacy `date`
+ const comparisonDate = (typeof options === 'object' ? options.comparisonDate : options) || Date().toString();
+
+ const comparison = toDate(comparisonDate);
+ const original = toDate(date);
+
return !!(original && comparison && original > comparison);
}
diff --git a/src/lib/isAlpha.js b/src/lib/isAlpha.js
index 1cc8f06b8..e961e64eb 100644
--- a/src/lib/isAlpha.js
+++ b/src/lib/isAlpha.js
@@ -1,10 +1,26 @@
import assertString from './util/assertString';
import { alpha } from './alpha';
-export default function isAlpha(str, locale = 'en-US') {
- assertString(str);
+export default function isAlpha(_str, locale = 'en-US', options = {}) {
+ assertString(_str);
+
+ let str = _str;
+ const { ignore } = options;
+
+ if (ignore) {
+ if (ignore instanceof RegExp) {
+ str = str.replace(ignore, '');
+ } else if (typeof ignore === 'string') {
+ str = str.replace(new RegExp(`[${ignore.replace(/[-[\]{}()*+?.,\\^$|#\\s]/g, '\\$&')}]`, 'g'), ''); // escape regex for ignore
+ } else {
+ throw new Error('ignore should be instance of a String or RegExp');
+ }
+ }
+
if (locale in alpha) {
return alpha[locale].test(str);
}
throw new Error(`Invalid locale '${locale}'`);
}
+
+export const locales = Object.keys(alpha);
diff --git a/src/lib/isAlphanumeric.js b/src/lib/isAlphanumeric.js
index b29f8174d..b259ab908 100644
--- a/src/lib/isAlphanumeric.js
+++ b/src/lib/isAlphanumeric.js
@@ -1,10 +1,26 @@
import assertString from './util/assertString';
import { alphanumeric } from './alpha';
-export default function isAlphanumeric(str, locale = 'en-US') {
- assertString(str);
+export default function isAlphanumeric(_str, locale = 'en-US', options = {}) {
+ assertString(_str);
+
+ let str = _str;
+ const { ignore } = options;
+
+ if (ignore) {
+ if (ignore instanceof RegExp) {
+ str = str.replace(ignore, '');
+ } else if (typeof ignore === 'string') {
+ str = str.replace(new RegExp(`[${ignore.replace(/[-[\]{}()*+?.,\\^$|#\\s]/g, '\\$&')}]`, 'g'), ''); // escape regex for ignore
+ } else {
+ throw new Error('ignore should be instance of a String or RegExp');
+ }
+ }
+
if (locale in alphanumeric) {
return alphanumeric[locale].test(str);
}
throw new Error(`Invalid locale '${locale}'`);
}
+
+export const locales = Object.keys(alphanumeric);
diff --git a/src/lib/isBIC.js b/src/lib/isBIC.js
new file mode 100644
index 000000000..b0f586728
--- /dev/null
+++ b/src/lib/isBIC.js
@@ -0,0 +1,19 @@
+import assertString from './util/assertString';
+import { CountryCodes } from './isISO31661Alpha2';
+
+// https://en.wikipedia.org/wiki/ISO_9362
+const isBICReg = /^[A-Za-z]{6}[A-Za-z0-9]{2}([A-Za-z0-9]{3})?$/;
+
+export default function isBIC(str) {
+ assertString(str);
+
+ // toUpperCase() should be removed when a new major version goes out that changes
+ // the regex to [A-Z] (per the spec).
+ const countryCode = str.slice(4, 6).toUpperCase();
+
+ if (!CountryCodes.has(countryCode) && countryCode !== 'XK') {
+ return false;
+ }
+
+ return isBICReg.test(str);
+}
diff --git a/src/lib/isBase32.js b/src/lib/isBase32.js
new file mode 100644
index 000000000..5e2969cbc
--- /dev/null
+++ b/src/lib/isBase32.js
@@ -0,0 +1,24 @@
+import assertString from './util/assertString';
+import merge from './util/merge';
+
+const base32 = /^[A-Z2-7]+=*$/;
+const crockfordBase32 = /^[A-HJKMNP-TV-Z0-9]+$/;
+
+const defaultBase32Options = {
+ crockford: false,
+};
+
+export default function isBase32(str, options) {
+ assertString(str);
+ options = merge(options, defaultBase32Options);
+
+ if (options.crockford) {
+ return crockfordBase32.test(str);
+ }
+
+ const len = str.length;
+ if (len % 8 === 0 && base32.test(str)) {
+ return true;
+ }
+ return false;
+}
diff --git a/src/lib/isBase58.js b/src/lib/isBase58.js
new file mode 100644
index 000000000..05c46dc18
--- /dev/null
+++ b/src/lib/isBase58.js
@@ -0,0 +1,12 @@
+import assertString from './util/assertString';
+
+// Accepted chars - 123456789ABCDEFGH JKLMN PQRSTUVWXYZabcdefghijk mnopqrstuvwxyz
+const base58Reg = /^[A-HJ-NP-Za-km-z1-9]*$/;
+
+export default function isBase58(str) {
+ assertString(str);
+ if (base58Reg.test(str)) {
+ return true;
+ }
+ return false;
+}
diff --git a/src/lib/isBase64.js b/src/lib/isBase64.js
index 73d8ec0b7..fd876e4c0 100644
--- a/src/lib/isBase64.js
+++ b/src/lib/isBase64.js
@@ -1,15 +1,25 @@
import assertString from './util/assertString';
+import merge from './util/merge';
-const notBase64 = /[^A-Z0-9+\/=]/i;
+const base64WithPadding = /^[A-Za-z0-9+/]+={0,2}$/;
+const base64WithoutPadding = /^[A-Za-z0-9+/]+$/;
+const base64UrlWithPadding = /^[A-Za-z0-9_-]+={0,2}$/;
+const base64UrlWithoutPadding = /^[A-Za-z0-9_-]+$/;
-export default function isBase64(str) {
+export default function isBase64(str, options) {
assertString(str);
- const len = str.length;
- if (!len || len % 4 !== 0 || notBase64.test(str)) {
- return false;
+ options = merge(options, { urlSafe: false, padding: !options?.urlSafe });
+
+ if (str === '') return true;
+
+ if (options.padding && str.length % 4 !== 0) return false;
+
+ let regex;
+ if (options.urlSafe) {
+ regex = options.padding ? base64UrlWithPadding : base64UrlWithoutPadding;
+ } else {
+ regex = options.padding ? base64WithPadding : base64WithoutPadding;
}
- const firstPaddingChar = str.indexOf('=');
- return firstPaddingChar === -1 ||
- firstPaddingChar === len - 1 ||
- (firstPaddingChar === len - 2 && str[len - 1] === '=');
+
+ return (!options.padding || str.length % 4 === 0) && regex.test(str);
}
diff --git a/src/lib/isBefore.js b/src/lib/isBefore.js
index 8314f0e14..977d0ad1a 100644
--- a/src/lib/isBefore.js
+++ b/src/lib/isBefore.js
@@ -1,9 +1,12 @@
-import assertString from './util/assertString';
import toDate from './toDate';
-export default function isBefore(str, date = String(new Date())) {
- assertString(str);
- const comparison = toDate(date);
- const original = toDate(str);
+export default function isBefore(date, options) {
+ // For backwards compatibility:
+ // isBefore(str [, date]), i.e. `options` could be used as argument for the legacy `date`
+ const comparisonDate = (typeof options === 'object' ? options.comparisonDate : options) || Date().toString();
+
+ const comparison = toDate(comparisonDate);
+ const original = toDate(date);
+
return !!(original && comparison && original < comparison);
}
diff --git a/src/lib/isBoolean.js b/src/lib/isBoolean.js
index e7cb27dfa..40ebd3504 100644
--- a/src/lib/isBoolean.js
+++ b/src/lib/isBoolean.js
@@ -1,6 +1,16 @@
import assertString from './util/assertString';
+import includes from './util/includesArray';
-export default function isBoolean(str) {
+const defaultOptions = { loose: false };
+const strictBooleans = ['true', 'false', '1', '0'];
+const looseBooleans = [...strictBooleans, 'yes', 'no'];
+
+export default function isBoolean(str, options = defaultOptions) {
assertString(str);
- return (['true', 'false', '1', '0'].indexOf(str) >= 0);
+
+ if (options.loose) {
+ return includes(looseBooleans, str.toLowerCase());
+ }
+
+ return includes(strictBooleans, str);
}
diff --git a/src/lib/isBtcAddress.js b/src/lib/isBtcAddress.js
new file mode 100644
index 000000000..1a309e682
--- /dev/null
+++ b/src/lib/isBtcAddress.js
@@ -0,0 +1,9 @@
+import assertString from './util/assertString';
+
+const bech32 = /^(bc1|tb1|bc1p|tb1p)[ac-hj-np-z02-9]{39,58}$/;
+const base58 = /^(1|2|3|m)[A-HJ-NP-Za-km-z1-9]{25,39}$/;
+
+export default function isBtcAddress(str) {
+ assertString(str);
+ return bech32.test(str) || base58.test(str);
+}
diff --git a/src/lib/isCreditCard.js b/src/lib/isCreditCard.js
index 085a4d3e6..938679d39 100644
--- a/src/lib/isCreditCard.js
+++ b/src/lib/isCreditCard.js
@@ -1,33 +1,42 @@
import assertString from './util/assertString';
+import isLuhnValid from './isLuhnNumber';
-/* eslint-disable max-len */
-const creditCard = /^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|(222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\d{3})\d{11}|62[0-9]{14})$/;
-/* eslint-enable max-len */
+const cards = {
+ amex: /^3[47][0-9]{13}$/,
+ dinersclub: /^3(?:0[0-5]|[68][0-9])[0-9]{11}$/,
+ discover: /^6(?:011|5[0-9][0-9])[0-9]{12,15}$/,
+ jcb: /^(?:2131|1800|35\d{3})\d{11}$/,
+ mastercard: /^5[1-5][0-9]{2}|(222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}$/, // /^[25][1-7][0-9]{14}$/;
+ unionpay: /^(6[27][0-9]{14}|^(81[0-9]{14,17}))$/,
+ visa: /^(?:4[0-9]{12})(?:[0-9]{3,6})?$/,
+};
-export default function isCreditCard(str) {
- assertString(str);
- const sanitized = str.replace(/[- ]+/g, '');
- if (!creditCard.test(sanitized)) {
- return false;
+const allCards = (() => {
+ const tmpCardsArray = [];
+ for (const cardProvider in cards) {
+ // istanbul ignore else
+ if (cards.hasOwnProperty(cardProvider)) {
+ tmpCardsArray.push(cards[cardProvider]);
+ }
}
- let sum = 0;
- let digit;
- let tmpNum;
- let shouldDouble;
- for (let i = sanitized.length - 1; i >= 0; i--) {
- digit = sanitized.substring(i, (i + 1));
- tmpNum = parseInt(digit, 10);
- if (shouldDouble) {
- tmpNum *= 2;
- if (tmpNum >= 10) {
- sum += ((tmpNum % 10) + 1);
- } else {
- sum += tmpNum;
- }
- } else {
- sum += tmpNum;
+ return tmpCardsArray;
+})();
+
+export default function isCreditCard(card, options = {}) {
+ assertString(card);
+ const { provider } = options;
+ const sanitized = card.replace(/[- ]+/g, '');
+ if (provider && provider.toLowerCase() in cards) {
+ // specific provider in the list
+ if (!(cards[provider.toLowerCase()].test(sanitized))) {
+ return false;
}
- shouldDouble = !shouldDouble;
+ } else if (provider && !(provider.toLowerCase() in cards)) {
+ /* specific provider not in the list */
+ throw new Error(`${provider} is not a valid credit card provider.`);
+ } else if (!allCards.some(cardProvider => cardProvider.test(sanitized))) {
+ // no specific provider
+ return false;
}
- return !!((sum % 10) === 0 ? sanitized : false);
+ return isLuhnValid(card);
}
diff --git a/src/lib/isCurrency.js b/src/lib/isCurrency.js
old mode 100644
new mode 100755
index 8cea9fbda..54b1f7c7c
--- a/src/lib/isCurrency.js
+++ b/src/lib/isCurrency.js
@@ -4,8 +4,9 @@ import assertString from './util/assertString';
function currencyRegex(options) {
let decimal_digits = `\\d{${options.digits_after_decimal[0]}}`;
options.digits_after_decimal.forEach((digit, index) => { if (index !== 0) decimal_digits = `${decimal_digits}|\\d{${digit}}`; });
+
const symbol =
- `(\\${options.symbol.replace(/\./g, '\\.')})${(options.require_symbol ? '' : '?')}`,
+ `(${options.symbol.replace(/\W/, m => `\\${m}`)})${(options.require_symbol ? '' : '?')}`,
negative = '-?',
whole_dollar_amount_without_sep = '[1-9]\\d*',
whole_dollar_amount_with_sep = `[1-9]\\d{0,2}(\\${options.thousands_separator}\\d{3})*`,
diff --git a/src/lib/isDataURI.js b/src/lib/isDataURI.js
index 77b40c5f5..506544807 100644
--- a/src/lib/isDataURI.js
+++ b/src/lib/isDataURI.js
@@ -1,8 +1,38 @@
import assertString from './util/assertString';
-const dataURI = /^\s*data:([a-z]+\/[a-z0-9\-\+]+(;[a-z\-]+=[a-z0-9\-]+)?)?(;base64)?,[a-z0-9!\$&',\(\)\*\+,;=\-\._~:@\/\?%\s]*\s*$/i; // eslint-disable-line max-len
+const validMediaType = /^[a-z]+\/[a-z0-9\-\+\._]+$/i;
+
+const validAttribute = /^[a-z\-]+=[a-z0-9\-]+$/i;
+
+const validData = /^[a-z0-9!\$&'\(\)\*\+,;=\-\._~:@\/\?%\s]*$/i;
export default function isDataURI(str) {
assertString(str);
- return dataURI.test(str);
+ let data = str.split(',');
+ if (data.length < 2) {
+ return false;
+ }
+ const attributes = data.shift().trim().split(';');
+ const schemeAndMediaType = attributes.shift();
+ if (schemeAndMediaType.slice(0, 5) !== 'data:') {
+ return false;
+ }
+ const mediaType = schemeAndMediaType.slice(5);
+ if (mediaType !== '' && !validMediaType.test(mediaType)) {
+ return false;
+ }
+ for (let i = 0; i < attributes.length; i++) {
+ if (
+ !(i === attributes.length - 1 && attributes[i].toLowerCase() === 'base64') &&
+ !validAttribute.test(attributes[i])
+ ) {
+ return false;
+ }
+ }
+ for (let i = 0; i < data.length; i++) {
+ if (!validData.test(data[i])) {
+ return false;
+ }
+ }
+ return true;
}
diff --git a/src/lib/isDate.js b/src/lib/isDate.js
new file mode 100644
index 000000000..3a1e4afd2
--- /dev/null
+++ b/src/lib/isDate.js
@@ -0,0 +1,94 @@
+import merge from './util/merge';
+
+const default_date_options = {
+ format: 'YYYY/MM/DD',
+ delimiters: ['/', '-'],
+ strictMode: false,
+};
+
+function isValidFormat(format) {
+ return /(^(y{4}|y{2})[.\/-](m{1,2})[.\/-](d{1,2})$)|(^(m{1,2})[.\/-](d{1,2})[.\/-]((y{4}|y{2})$))|(^(d{1,2})[.\/-](m{1,2})[.\/-]((y{4}|y{2})$))/gi.test(format);
+}
+
+function zip(date, format) {
+ const zippedArr = [],
+ len = Math.max(date.length, format.length);
+
+ for (let i = 0; i < len; i++) {
+ zippedArr.push([date[i], format[i]]);
+ }
+
+ return zippedArr;
+}
+
+export default function isDate(input, options) {
+ if (typeof options === 'string') { // Allow backward compatibility for old format isDate(input [, format])
+ options = merge({ format: options }, default_date_options);
+ } else {
+ options = merge(options, default_date_options);
+ }
+ if (typeof input === 'string' && isValidFormat(options.format)) {
+ if (options.strictMode && input.length !== options.format.length) return false;
+ const formatDelimiter = options.delimiters
+ .find(delimiter => options.format.indexOf(delimiter) !== -1);
+ const dateDelimiter = options.strictMode
+ ? formatDelimiter
+ : options.delimiters.find(delimiter => input.indexOf(delimiter) !== -1);
+ const dateAndFormat = zip(
+ input.split(dateDelimiter),
+ options.format.toLowerCase().split(formatDelimiter)
+ );
+ const dateObj = {};
+
+ for (const [dateWord, formatWord] of dateAndFormat) {
+ if (!dateWord || !formatWord || dateWord.length !== formatWord.length) {
+ return false;
+ }
+
+ dateObj[formatWord.charAt(0)] = dateWord;
+ }
+
+ let fullYear = dateObj.y;
+
+ // Check if the year starts with a hyphen
+ if (fullYear.startsWith('-')) {
+ return false; // Hyphen before year is not allowed
+ }
+
+ if (dateObj.y.length === 2) {
+ const parsedYear = parseInt(dateObj.y, 10);
+
+ if (isNaN(parsedYear)) {
+ return false;
+ }
+
+ const currentYearLastTwoDigits = new Date().getFullYear() % 100;
+
+ if (parsedYear < currentYearLastTwoDigits) {
+ fullYear = `20${dateObj.y}`;
+ } else {
+ fullYear = `19${dateObj.y}`;
+ }
+ }
+
+ let month = dateObj.m;
+
+ if (dateObj.m.length === 1) {
+ month = `0${dateObj.m}`;
+ }
+
+ let day = dateObj.d;
+
+ if (dateObj.d.length === 1) {
+ day = `0${dateObj.d}`;
+ }
+
+ return new Date(`${fullYear}-${month}-${day}T00:00:00.000Z`).getUTCDate() === +dateObj.d;
+ }
+
+ if (!options.strictMode) {
+ return Object.prototype.toString.call(input) === '[object Date]' && isFinite(input);
+ }
+
+ return false;
+}
diff --git a/src/lib/isDecimal.js b/src/lib/isDecimal.js
index f3f5734c2..474eff2b0 100644
--- a/src/lib/isDecimal.js
+++ b/src/lib/isDecimal.js
@@ -1,5 +1,6 @@
import merge from './util/merge';
import assertString from './util/assertString';
+import includes from './util/includesArray';
import { decimal } from './alpha';
function decimalRegExp(options) {
@@ -19,7 +20,7 @@ export default function isDecimal(str, options) {
assertString(str);
options = merge(options, default_decimal_options);
if (options.locale in decimal) {
- return !blacklist.includes(str.replace(/ /g, '')) && decimalRegExp(options).test(str);
+ return !includes(blacklist, str.replace(/ /g, '')) && decimalRegExp(options).test(str);
}
throw new Error(`Invalid locale '${options.locale}'`);
}
diff --git a/src/lib/isEAN.js b/src/lib/isEAN.js
new file mode 100644
index 000000000..731932ad3
--- /dev/null
+++ b/src/lib/isEAN.js
@@ -0,0 +1,75 @@
+/**
+ * The most commonly used EAN standard is
+ * the thirteen-digit EAN-13, while the
+ * less commonly used 8-digit EAN-8 barcode was
+ * introduced for use on small packages.
+ * Also EAN/UCC-14 is used for Grouping of individual
+ * trade items above unit level(Intermediate, Carton or Pallet).
+ * For more info about EAN-14 checkout: https://www.gtin.info/itf-14-barcodes/
+ * EAN consists of:
+ * GS1 prefix, manufacturer code, product code and check digit
+ * Reference: https://en.wikipedia.org/wiki/International_Article_Number
+ * Reference: https://www.gtin.info/
+ */
+
+import assertString from './util/assertString';
+
+/**
+ * Define EAN Lengths; 8 for EAN-8; 13 for EAN-13; 14 for EAN-14
+ * and Regular Expression for valid EANs (EAN-8, EAN-13, EAN-14),
+ * with exact numeric matching of 8 or 13 or 14 digits [0-9]
+ */
+const LENGTH_EAN_8 = 8;
+const LENGTH_EAN_14 = 14;
+const validEanRegex = /^(\d{8}|\d{13}|\d{14})$/;
+
+
+/**
+ * Get position weight given:
+ * EAN length and digit index/position
+ *
+ * @param {number} length
+ * @param {number} index
+ * @return {number}
+ */
+function getPositionWeightThroughLengthAndIndex(length, index) {
+ if (length === LENGTH_EAN_8 || length === LENGTH_EAN_14) {
+ return (index % 2 === 0) ? 3 : 1;
+ }
+
+ return (index % 2 === 0) ? 1 : 3;
+}
+
+/**
+ * Calculate EAN Check Digit
+ * Reference: https://en.wikipedia.org/wiki/International_Article_Number#Calculation_of_checksum_digit
+ *
+ * @param {string} ean
+ * @return {number}
+ */
+function calculateCheckDigit(ean) {
+ const checksum = ean
+ .slice(0, -1)
+ .split('')
+ .map((char, index) => Number(char) * getPositionWeightThroughLengthAndIndex(ean.length, index))
+ .reduce((acc, partialSum) => acc + partialSum, 0);
+
+ const remainder = 10 - (checksum % 10);
+
+ return remainder < 10 ? remainder : 0;
+}
+
+/**
+ * Check if string is valid EAN:
+ * Matches EAN-8/EAN-13/EAN-14 regex
+ * Has valid check digit.
+ *
+ * @param {string} str
+ * @return {boolean}
+ */
+export default function isEAN(str) {
+ assertString(str);
+ const actualCheckDigit = Number(str.slice(-1));
+
+ return validEanRegex.test(str) && actualCheckDigit === calculateCheckDigit(str);
+}
diff --git a/src/lib/isEmail.js b/src/lib/isEmail.js
index e3d9dd9c0..abe465052 100644
--- a/src/lib/isEmail.js
+++ b/src/lib/isEmail.js
@@ -1,58 +1,172 @@
import assertString from './util/assertString';
+import checkHost from './util/checkHost';
-import merge from './util/merge';
import isByteLength from './isByteLength';
import isFQDN from './isFQDN';
+import isIP from './isIP';
+import merge from './util/merge';
const default_email_options = {
allow_display_name: false,
+ allow_underscores: false,
require_display_name: false,
allow_utf8_local_part: true,
require_tld: true,
+ blacklisted_chars: '',
+ ignore_max_length: false,
+ host_blacklist: [],
+ host_whitelist: [],
};
/* eslint-disable max-len */
/* eslint-disable no-control-regex */
-const displayName = /^[a-z\d!#\$%&'\*\+\-\/=\?\^_`{\|}~\.\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+[a-z\d!#\$%&'\*\+\-\/=\?\^_`{\|}~\,\.\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF\s]*<(.+)>$/i;
+const splitNameAddress = /^([^\x00-\x1F\x7F-\x9F\cX]+)]/.test(display_name_without_quotes);
+ if (contains_illegal) {
+ // if contains illegal characters,
+ // must to be enclosed in double-quotes, otherwise it's not a valid display name
+ if (display_name_without_quotes === display_name) {
+ return false;
+ }
+
+ // the quotes in display name must start with character symbol \
+ const all_start_with_back_slash =
+ display_name_without_quotes.split('"').length === display_name_without_quotes.split('\\"').length;
+ if (!all_start_with_back_slash) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
export default function isEmail(str, options) {
assertString(str);
options = merge(options, default_email_options);
if (options.require_display_name || options.allow_display_name) {
- const display_email = str.match(displayName);
+ const display_email = str.match(splitNameAddress);
if (display_email) {
- str = display_email[1];
+ let display_name = display_email[1];
+
+ // Remove display name and angle brackets to get email address
+ // Can be done in the regex but will introduce a ReDOS (See #1597 for more info)
+ str = str.replace(display_name, '').replace(/(^<|>$)/g, '');
+
+ // sometimes need to trim the last space to get the display name
+ // because there may be a space between display name and email address
+ // eg. myname
+ // the display name is `myname` instead of `myname `, so need to trim the last space
+ if (display_name.endsWith(' ')) {
+ display_name = display_name.slice(0, -1);
+ }
+
+ if (!validateDisplayName(display_name)) {
+ return false;
+ }
} else if (options.require_display_name) {
return false;
}
}
+ if (!options.ignore_max_length && str.length > defaultMaxEmailLength) {
+ return false;
+ }
const parts = str.split('@');
const domain = parts.pop();
- let user = parts.join('@');
-
const lower_domain = domain.toLowerCase();
- if (lower_domain === 'gmail.com' || lower_domain === 'googlemail.com') {
- user = user.replace(/\./g, '').toLowerCase();
+
+ if (options.host_blacklist.length > 0 && checkHost(lower_domain, options.host_blacklist)) {
+ return false;
}
- if (!isByteLength(user, { max: 64 }) ||
- !isByteLength(domain, { max: 254 })) {
+ if (options.host_whitelist.length > 0 && !checkHost(lower_domain, options.host_whitelist)) {
return false;
}
- if (!isFQDN(domain, { require_tld: options.require_tld })) {
+ let user = parts.join('@');
+
+ if (options.domain_specific_validation && (lower_domain === 'gmail.com' || lower_domain === 'googlemail.com')) {
+ /*
+ Previously we removed dots for gmail addresses before validating.
+ This was removed because it allows `multiple..dots@gmail.com`
+ to be reported as valid, but it is not.
+ Gmail only normalizes single dots, removing them from here is pointless,
+ should be done in normalizeEmail
+ */
+ user = user.toLowerCase();
+
+ // Removing sub-address from username before gmail validation
+ const username = user.split('+')[0];
+
+ // Dots are not included in gmail length restriction
+ if (!isByteLength(username.replace(/\./g, ''), { min: 6, max: 30 })) {
+ return false;
+ }
+
+ const user_parts = username.split('.');
+ for (let i = 0; i < user_parts.length; i++) {
+ if (!gmailUserPart.test(user_parts[i])) {
+ return false;
+ }
+ }
+ }
+
+ if (options.ignore_max_length === false && (
+ !isByteLength(user, { max: 64 }) ||
+ !isByteLength(domain, { max: 254 }))
+ ) {
return false;
}
- if (user[0] === '"') {
+ if (!isFQDN(domain, {
+ require_tld: options.require_tld,
+ ignore_max_length: options.ignore_max_length,
+ allow_underscores: options.allow_underscores,
+ })) {
+ if (!options.allow_ip_domain) {
+ return false;
+ }
+
+ if (!isIP(domain)) {
+ if (!domain.startsWith('[') || !domain.endsWith(']')) {
+ return false;
+ }
+
+ let noBracketdomain = domain.slice(1, -1);
+
+ if (noBracketdomain.length === 0 || !isIP(noBracketdomain)) {
+ return false;
+ }
+ }
+ }
+
+ if (options.blacklisted_chars) {
+ if (user.search(new RegExp(`[${options.blacklisted_chars}]+`, 'g')) !== -1) return false;
+ }
+
+ if (user[0] === '"' && user[user.length - 1] === '"') {
user = user.slice(1, user.length - 1);
return options.allow_utf8_local_part ?
quotedEmailUserUtf8.test(user) :
diff --git a/src/lib/isEmpty.js b/src/lib/isEmpty.js
index 1b1d906b3..eeb5997d0 100644
--- a/src/lib/isEmpty.js
+++ b/src/lib/isEmpty.js
@@ -1,6 +1,13 @@
import assertString from './util/assertString';
+import merge from './util/merge';
-export default function isEmpty(str) {
+const default_is_empty_options = {
+ ignore_whitespace: false,
+};
+
+export default function isEmpty(str, options) {
assertString(str);
- return str.length === 0;
+ options = merge(options, default_is_empty_options);
+
+ return (options.ignore_whitespace ? str.trim().length : str.length) === 0;
}
diff --git a/src/lib/isEthereumAddress.js b/src/lib/isEthereumAddress.js
new file mode 100644
index 000000000..e538760bf
--- /dev/null
+++ b/src/lib/isEthereumAddress.js
@@ -0,0 +1,8 @@
+import assertString from './util/assertString';
+
+const eth = /^(0x)[0-9a-f]{40}$/i;
+
+export default function isEthereumAddress(str) {
+ assertString(str);
+ return eth.test(str);
+}
diff --git a/src/lib/isFQDN.js b/src/lib/isFQDN.js
index 66d9ca5f3..eb6928fda 100644
--- a/src/lib/isFQDN.js
+++ b/src/lib/isFQDN.js
@@ -5,6 +5,9 @@ const default_fqdn_options = {
require_tld: true,
allow_underscores: false,
allow_trailing_dot: false,
+ allow_numeric_tld: false,
+ allow_wildcard: false,
+ ignore_max_length: false,
};
export default function isFQDN(str, options) {
@@ -15,32 +18,59 @@ export default function isFQDN(str, options) {
if (options.allow_trailing_dot && str[str.length - 1] === '.') {
str = str.substring(0, str.length - 1);
}
+
+ /* Remove the optional wildcard before checking validity */
+ if (options.allow_wildcard === true && str.indexOf('*.') === 0) {
+ str = str.substring(2);
+ }
+
const parts = str.split('.');
+ const tld = parts[parts.length - 1];
+
if (options.require_tld) {
- const tld = parts.pop();
- if (!parts.length || !/^([a-z\u00a1-\uffff]{2,}|xn[a-z0-9-]{2,})$/i.test(tld)) {
+ // disallow fqdns without tld
+ if (parts.length < 2) {
+ return false;
+ }
+
+ if (!options.allow_numeric_tld && !/^([a-z\u00A1-\u00A8\u00AA-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]{2,}|xn[a-z0-9-]{2,})$/i.test(tld)) {
return false;
}
+
// disallow spaces
- if (/[\s\u2002-\u200B\u202F\u205F\u3000\uFEFF\uDB40\uDC20]/.test(tld)) {
+ if (/\s/.test(tld)) {
return false;
}
}
- for (let part, i = 0; i < parts.length; i++) {
- part = parts[i];
- if (options.allow_underscores) {
- part = part.replace(/_/g, '');
+
+ // reject numeric TLDs
+ if (!options.allow_numeric_tld && /^\d+$/.test(tld)) {
+ return false;
+ }
+
+ return parts.every((part) => {
+ if (part.length > 63 && !options.ignore_max_length) {
+ return false;
}
- if (!/^[a-z\u00a1-\uffff0-9-]+$/i.test(part)) {
+
+ if (!/^[a-z_\u00a1-\uffff0-9-]+$/i.test(part)) {
return false;
}
+
// disallow full-width chars
if (/[\uff01-\uff5e]/.test(part)) {
return false;
}
- if (part[0] === '-' || part[part.length - 1] === '-') {
+
+ // disallow parts starting or ending with hyphen
+ if (/^-|-$/.test(part)) {
+ return false;
+ }
+
+ if (!options.allow_underscores && /_/.test(part)) {
return false;
}
- }
- return true;
+
+ return true;
+ });
}
diff --git a/src/lib/isFloat.js b/src/lib/isFloat.js
index 97ff39519..84bdc782c 100644
--- a/src/lib/isFloat.js
+++ b/src/lib/isFloat.js
@@ -1,16 +1,20 @@
import assertString from './util/assertString';
+import isNullOrUndefined from './util/nullUndefinedCheck';
import { decimal } from './alpha';
export default function isFloat(str, options) {
assertString(str);
options = options || {};
const float = new RegExp(`^(?:[-+])?(?:[0-9]+)?(?:\\${options.locale ? decimal[options.locale] : '.'}[0-9]*)?(?:[eE][\\+\\-]?(?:[0-9]+))?$`);
- if (str === '' || str === '.' || str === '-' || str === '+') {
+ if (str === '' || str === '.' || str === ',' || str === '-' || str === '+') {
return false;
}
+ const value = parseFloat(str.replace(',', '.'));
return float.test(str) &&
- (!options.hasOwnProperty('min') || str >= options.min) &&
- (!options.hasOwnProperty('max') || str <= options.max) &&
- (!options.hasOwnProperty('lt') || str < options.lt) &&
- (!options.hasOwnProperty('gt') || str > options.gt);
+ (!options.hasOwnProperty('min') || isNullOrUndefined(options.min) || value >= options.min) &&
+ (!options.hasOwnProperty('max') || isNullOrUndefined(options.max) || value <= options.max) &&
+ (!options.hasOwnProperty('lt') || isNullOrUndefined(options.lt) || value < options.lt) &&
+ (!options.hasOwnProperty('gt') || isNullOrUndefined(options.gt) || value > options.gt);
}
+
+export const locales = Object.keys(decimal);
diff --git a/src/lib/isHSL.js b/src/lib/isHSL.js
new file mode 100644
index 000000000..05cb43962
--- /dev/null
+++ b/src/lib/isHSL.js
@@ -0,0 +1,19 @@
+import assertString from './util/assertString';
+
+
+const hslComma = /^hsla?\(((\+|\-)?([0-9]+(\.[0-9]+)?(e(\+|\-)?[0-9]+)?|\.[0-9]+(e(\+|\-)?[0-9]+)?))(deg|grad|rad|turn)?(,(\+|\-)?([0-9]+(\.[0-9]+)?(e(\+|\-)?[0-9]+)?|\.[0-9]+(e(\+|\-)?[0-9]+)?)%){2}(,((\+|\-)?([0-9]+(\.[0-9]+)?(e(\+|\-)?[0-9]+)?|\.[0-9]+(e(\+|\-)?[0-9]+)?)%?))?\)$/i;
+const hslSpace = /^hsla?\(((\+|\-)?([0-9]+(\.[0-9]+)?(e(\+|\-)?[0-9]+)?|\.[0-9]+(e(\+|\-)?[0-9]+)?))(deg|grad|rad|turn)?(\s(\+|\-)?([0-9]+(\.[0-9]+)?(e(\+|\-)?[0-9]+)?|\.[0-9]+(e(\+|\-)?[0-9]+)?)%){2}\s?(\/\s((\+|\-)?([0-9]+(\.[0-9]+)?(e(\+|\-)?[0-9]+)?|\.[0-9]+(e(\+|\-)?[0-9]+)?)%?)\s?)?\)$/i;
+
+
+export default function isHSL(str) {
+ assertString(str);
+
+ // Strip duplicate spaces before calling the validation regex (See #1598 for more info)
+ const strippedStr = str.replace(/\s+/g, ' ').replace(/\s?(hsla?\(|\)|,)\s?/ig, '$1');
+
+ if (strippedStr.indexOf(',') !== -1) {
+ return hslComma.test(strippedStr);
+ }
+
+ return hslSpace.test(strippedStr);
+}
diff --git a/src/lib/isHash.js b/src/lib/isHash.js
index 62af2d413..6efe7c035 100644
--- a/src/lib/isHash.js
+++ b/src/lib/isHash.js
@@ -18,6 +18,6 @@ const lengths = {
export default function isHash(str, algorithm) {
assertString(str);
- const hash = new RegExp(`^[a-f0-9]{${lengths[algorithm]}}$`);
+ const hash = new RegExp(`^[a-fA-F0-9]{${lengths[algorithm]}}$`);
return hash.test(str);
}
diff --git a/src/lib/isHexColor.js b/src/lib/isHexColor.js
index 01f9d0e85..21a037504 100644
--- a/src/lib/isHexColor.js
+++ b/src/lib/isHexColor.js
@@ -1,6 +1,6 @@
import assertString from './util/assertString';
-const hexcolor = /^#?([0-9A-F]{3}|[0-9A-F]{6})$/i;
+const hexcolor = /^#?([0-9A-F]{3}|[0-9A-F]{4}|[0-9A-F]{6}|[0-9A-F]{8})$/i;
export default function isHexColor(str) {
assertString(str);
diff --git a/src/lib/isHexadecimal.js b/src/lib/isHexadecimal.js
index 1e4f17e03..19adb077a 100644
--- a/src/lib/isHexadecimal.js
+++ b/src/lib/isHexadecimal.js
@@ -1,6 +1,6 @@
import assertString from './util/assertString';
-const hexadecimal = /^[0-9A-F]+$/i;
+const hexadecimal = /^(0x|0h)?[0-9A-F]+$/i;
export default function isHexadecimal(str) {
assertString(str);
diff --git a/src/lib/isIBAN.js b/src/lib/isIBAN.js
new file mode 100644
index 000000000..ed0abd6c0
--- /dev/null
+++ b/src/lib/isIBAN.js
@@ -0,0 +1,184 @@
+import assertString from './util/assertString';
+import includes from './util/includesArray';
+
+/**
+ * List of country codes with
+ * corresponding IBAN regular expression
+ * Reference: https://en.wikipedia.org/wiki/International_Bank_Account_Number
+ */
+const ibanRegexThroughCountryCode = {
+ AD: /^(AD[0-9]{2})\d{8}[A-Z0-9]{12}$/,
+ AE: /^(AE[0-9]{2})\d{3}\d{16}$/,
+ AL: /^(AL[0-9]{2})\d{8}[A-Z0-9]{16}$/,
+ AT: /^(AT[0-9]{2})\d{16}$/,
+ AZ: /^(AZ[0-9]{2})[A-Z0-9]{4}\d{20}$/,
+ BA: /^(BA[0-9]{2})\d{16}$/,
+ BE: /^(BE[0-9]{2})\d{12}$/,
+ BG: /^(BG[0-9]{2})[A-Z]{4}\d{6}[A-Z0-9]{8}$/,
+ BH: /^(BH[0-9]{2})[A-Z]{4}[A-Z0-9]{14}$/,
+ BR: /^(BR[0-9]{2})\d{23}[A-Z]{1}[A-Z0-9]{1}$/,
+ BY: /^(BY[0-9]{2})[A-Z0-9]{4}\d{20}$/,
+ CH: /^(CH[0-9]{2})\d{5}[A-Z0-9]{12}$/,
+ CR: /^(CR[0-9]{2})\d{18}$/,
+ CY: /^(CY[0-9]{2})\d{8}[A-Z0-9]{16}$/,
+ CZ: /^(CZ[0-9]{2})\d{20}$/,
+ DE: /^(DE[0-9]{2})\d{18}$/,
+ DK: /^(DK[0-9]{2})\d{14}$/,
+ DO: /^(DO[0-9]{2})[A-Z]{4}\d{20}$/,
+ DZ: /^(DZ\d{24})$/,
+ EE: /^(EE[0-9]{2})\d{16}$/,
+ EG: /^(EG[0-9]{2})\d{25}$/,
+ ES: /^(ES[0-9]{2})\d{20}$/,
+ FI: /^(FI[0-9]{2})\d{14}$/,
+ FO: /^(FO[0-9]{2})\d{14}$/,
+ FR: /^(FR[0-9]{2})\d{10}[A-Z0-9]{11}\d{2}$/,
+ GB: /^(GB[0-9]{2})[A-Z]{4}\d{14}$/,
+ GE: /^(GE[0-9]{2})[A-Z0-9]{2}\d{16}$/,
+ GI: /^(GI[0-9]{2})[A-Z]{4}[A-Z0-9]{15}$/,
+ GL: /^(GL[0-9]{2})\d{14}$/,
+ GR: /^(GR[0-9]{2})\d{7}[A-Z0-9]{16}$/,
+ GT: /^(GT[0-9]{2})[A-Z0-9]{4}[A-Z0-9]{20}$/,
+ HR: /^(HR[0-9]{2})\d{17}$/,
+ HU: /^(HU[0-9]{2})\d{24}$/,
+ IE: /^(IE[0-9]{2})[A-Z]{4}\d{14}$/,
+ IL: /^(IL[0-9]{2})\d{19}$/,
+ IQ: /^(IQ[0-9]{2})[A-Z]{4}\d{15}$/,
+ IR: /^(IR[0-9]{2})0\d{2}0\d{18}$/,
+ IS: /^(IS[0-9]{2})\d{22}$/,
+ IT: /^(IT[0-9]{2})[A-Z]{1}\d{10}[A-Z0-9]{12}$/,
+ JO: /^(JO[0-9]{2})[A-Z]{4}\d{22}$/,
+ KW: /^(KW[0-9]{2})[A-Z]{4}[A-Z0-9]{22}$/,
+ KZ: /^(KZ[0-9]{2})\d{3}[A-Z0-9]{13}$/,
+ LB: /^(LB[0-9]{2})\d{4}[A-Z0-9]{20}$/,
+ LC: /^(LC[0-9]{2})[A-Z]{4}[A-Z0-9]{24}$/,
+ LI: /^(LI[0-9]{2})\d{5}[A-Z0-9]{12}$/,
+ LT: /^(LT[0-9]{2})\d{16}$/,
+ LU: /^(LU[0-9]{2})\d{3}[A-Z0-9]{13}$/,
+ LV: /^(LV[0-9]{2})[A-Z]{4}[A-Z0-9]{13}$/,
+ MA: /^(MA[0-9]{26})$/,
+ MC: /^(MC[0-9]{2})\d{10}[A-Z0-9]{11}\d{2}$/,
+ MD: /^(MD[0-9]{2})[A-Z0-9]{20}$/,
+ ME: /^(ME[0-9]{2})\d{18}$/,
+ MK: /^(MK[0-9]{2})\d{3}[A-Z0-9]{10}\d{2}$/,
+ MR: /^(MR[0-9]{2})\d{23}$/,
+ MT: /^(MT[0-9]{2})[A-Z]{4}\d{5}[A-Z0-9]{18}$/,
+ MU: /^(MU[0-9]{2})[A-Z]{4}\d{19}[A-Z]{3}$/,
+ MZ: /^(MZ[0-9]{2})\d{21}$/,
+ NL: /^(NL[0-9]{2})[A-Z]{4}\d{10}$/,
+ NO: /^(NO[0-9]{2})\d{11}$/,
+ PK: /^(PK[0-9]{2})[A-Z0-9]{4}\d{16}$/,
+ PL: /^(PL[0-9]{2})\d{24}$/,
+ PS: /^(PS[0-9]{2})[A-Z]{4}[A-Z0-9]{21}$/,
+ PT: /^(PT[0-9]{2})\d{21}$/,
+ QA: /^(QA[0-9]{2})[A-Z]{4}[A-Z0-9]{21}$/,
+ RO: /^(RO[0-9]{2})[A-Z]{4}[A-Z0-9]{16}$/,
+ RS: /^(RS[0-9]{2})\d{18}$/,
+ SA: /^(SA[0-9]{2})\d{2}[A-Z0-9]{18}$/,
+ SC: /^(SC[0-9]{2})[A-Z]{4}\d{20}[A-Z]{3}$/,
+ SE: /^(SE[0-9]{2})\d{20}$/,
+ SI: /^(SI[0-9]{2})\d{15}$/,
+ SK: /^(SK[0-9]{2})\d{20}$/,
+ SM: /^(SM[0-9]{2})[A-Z]{1}\d{10}[A-Z0-9]{12}$/,
+ SV: /^(SV[0-9]{2})[A-Z0-9]{4}\d{20}$/,
+ TL: /^(TL[0-9]{2})\d{19}$/,
+ TN: /^(TN[0-9]{2})\d{20}$/,
+ TR: /^(TR[0-9]{2})\d{5}[A-Z0-9]{17}$/,
+ UA: /^(UA[0-9]{2})\d{6}[A-Z0-9]{19}$/,
+ VA: /^(VA[0-9]{2})\d{18}$/,
+ VG: /^(VG[0-9]{2})[A-Z]{4}\d{16}$/,
+ XK: /^(XK[0-9]{2})\d{16}$/,
+};
+
+/**
+ * Check if the country codes passed are valid using the
+ * ibanRegexThroughCountryCode as a reference
+ *
+ * @param {array} countryCodeArray
+ * @return {boolean}
+ */
+
+function hasOnlyValidCountryCodes(countryCodeArray) {
+ const countryCodeArrayFilteredWithObjectIbanCode = countryCodeArray
+ .filter(countryCode => !(countryCode in ibanRegexThroughCountryCode));
+
+ if (countryCodeArrayFilteredWithObjectIbanCode.length > 0) {
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * Check whether string has correct universal IBAN format
+ * The IBAN consists of up to 34 alphanumeric characters, as follows:
+ * Country Code using ISO 3166-1 alpha-2, two letters
+ * check digits, two digits and
+ * Basic Bank Account Number (BBAN), up to 30 alphanumeric characters.
+ * NOTE: Permitted IBAN characters are: digits [0-9] and the 26 latin alphabetic [A-Z]
+ *
+ * @param {string} str - string under validation
+ * @param {object} options - object to pass the countries to be either whitelisted or blacklisted
+ * @return {boolean}
+ */
+function hasValidIbanFormat(str, options) {
+ // Strip white spaces and hyphens
+ const strippedStr = str.replace(/[\s\-]+/gi, '').toUpperCase();
+ const isoCountryCode = strippedStr.slice(0, 2).toUpperCase();
+
+ const isoCountryCodeInIbanRegexCodeObject = isoCountryCode in ibanRegexThroughCountryCode;
+
+ if (options.whitelist) {
+ if (!hasOnlyValidCountryCodes(options.whitelist)) {
+ return false;
+ }
+
+ const isoCountryCodeInWhiteList = includes(options.whitelist, isoCountryCode);
+
+ if (!isoCountryCodeInWhiteList) {
+ return false;
+ }
+ }
+
+ if (options.blacklist) {
+ const isoCountryCodeInBlackList = includes(options.blacklist, isoCountryCode);
+
+ if (isoCountryCodeInBlackList) {
+ return false;
+ }
+ }
+
+ return (isoCountryCodeInIbanRegexCodeObject) &&
+ ibanRegexThroughCountryCode[isoCountryCode].test(strippedStr);
+}
+
+/**
+ * Check whether string has valid IBAN Checksum
+ * by performing basic mod-97 operation and
+ * the remainder should equal 1
+ * -- Start by rearranging the IBAN by moving the four initial characters to the end of the string
+ * -- Replace each letter in the string with two digits, A -> 10, B = 11, Z = 35
+ * -- Interpret the string as a decimal integer and
+ * -- compute the remainder on division by 97 (mod 97)
+ * Reference: https://en.wikipedia.org/wiki/International_Bank_Account_Number
+ *
+ * @param {string} str
+ * @return {boolean}
+ */
+function hasValidIbanChecksum(str) {
+ const strippedStr = str.replace(/[^A-Z0-9]+/gi, '').toUpperCase(); // Keep only digits and A-Z latin alphabetic
+ const rearranged = strippedStr.slice(4) + strippedStr.slice(0, 4);
+ const alphaCapsReplacedWithDigits = rearranged.replace(/[A-Z]/g, char => char.charCodeAt(0) - 55);
+
+ const remainder = alphaCapsReplacedWithDigits.match(/\d{1,7}/g)
+ .reduce((acc, value) => Number(acc + value) % 97, '');
+
+ return remainder === 1;
+}
+
+export default function isIBAN(str, options = {}) {
+ assertString(str);
+
+ return hasValidIbanFormat(str, options) && hasValidIbanChecksum(str);
+}
+
+export const locales = Object.keys(ibanRegexThroughCountryCode);
diff --git a/src/lib/isIMEI.js b/src/lib/isIMEI.js
new file mode 100644
index 000000000..984b4fb88
--- /dev/null
+++ b/src/lib/isIMEI.js
@@ -0,0 +1,50 @@
+import assertString from './util/assertString';
+
+
+let imeiRegexWithoutHyphens = /^[0-9]{15}$/;
+let imeiRegexWithHyphens = /^\d{2}-\d{6}-\d{6}-\d{1}$/;
+
+
+export default function isIMEI(str, options) {
+ assertString(str);
+ options = options || {};
+
+ // default regex for checking imei is the one without hyphens
+
+ let imeiRegex = imeiRegexWithoutHyphens;
+
+ if (options.allow_hyphens) {
+ imeiRegex = imeiRegexWithHyphens;
+ }
+
+
+ if (!imeiRegex.test(str)) {
+ return false;
+ }
+
+ str = str.replace(/-/g, '');
+
+ let sum = 0,
+ mul = 2,
+ l = 14;
+
+ for (let i = 0; i < l; i++) {
+ const digit = str.substring(l - i - 1, l - i);
+ const tp = parseInt(digit, 10) * mul;
+ if (tp >= 10) {
+ sum += (tp % 10) + 1;
+ } else {
+ sum += tp;
+ }
+ if (mul === 1) {
+ mul += 1;
+ } else {
+ mul -= 1;
+ }
+ }
+ const chk = ((10 - (sum % 10)) % 10);
+ if (chk !== parseInt(str.substring(14, 15), 10)) {
+ return false;
+ }
+ return true;
+}
diff --git a/src/lib/isIP.js b/src/lib/isIP.js
index e4776e491..da9bb6ca9 100644
--- a/src/lib/isIP.js
+++ b/src/lib/isIP.js
@@ -1,65 +1,67 @@
import assertString from './util/assertString';
+/**
+11.3. Examples
-const ipv4Maybe = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/;
-const ipv6Block = /^[0-9A-F]{1,4}$/i;
+ The following addresses
-export default function isIP(str, version = '') {
- assertString(str);
- version = String(version);
- if (!version) {
- return isIP(str, 4) || isIP(str, 6);
- } else if (version === '4') {
- if (!ipv4Maybe.test(str)) {
- return false;
- }
- const parts = str.split('.').sort((a, b) => a - b);
- return parts[3] <= 255;
- } else if (version === '6') {
- const blocks = str.split(':');
- let foundOmissionBlock = false; // marker to indicate ::
+ fe80::1234 (on the 1st link of the node)
+ ff02::5678 (on the 5th link of the node)
+ ff08::9abc (on the 10th organization of the node)
+
+ would be represented as follows:
+
+ fe80::1234%1
+ ff02::5678%5
+ ff08::9abc%10
+
+ (Here we assume a natural translation from a zone index to the
+ part, where the Nth zone of any scope is translated into
+ "N".)
+
+ If we use interface names as , those addresses could also be
+ represented as follows:
+
+ fe80::1234%ne0
+ ff02::5678%pvc1.3
+ ff08::9abc%interface10
+
+ where the interface "ne0" belongs to the 1st link, "pvc1.3" belongs
+ to the 5th link, and "interface10" belongs to the 10th organization.
+ * * */
+const IPv4SegmentFormat = '(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])';
+const IPv4AddressFormat = `(${IPv4SegmentFormat}[.]){3}${IPv4SegmentFormat}`;
+const IPv4AddressRegExp = new RegExp(`^${IPv4AddressFormat}$`);
- // At least some OS accept the last 32 bits of an IPv6 address
- // (i.e. 2 of the blocks) in IPv4 notation, and RFC 3493 says
- // that '::ffff:a.b.c.d' is valid for IPv4-mapped IPv6 addresses,
- // and '::a.b.c.d' is deprecated, but also valid.
- const foundIPv4TransitionBlock = isIP(blocks[blocks.length - 1], 4);
- const expectedNumberOfBlocks = foundIPv4TransitionBlock ? 7 : 8;
+const IPv6SegmentFormat = '(?:[0-9a-fA-F]{1,4})';
+const IPv6AddressRegExp = new RegExp('^(' +
+ `(?:${IPv6SegmentFormat}:){7}(?:${IPv6SegmentFormat}|:)|` +
+ `(?:${IPv6SegmentFormat}:){6}(?:${IPv4AddressFormat}|:${IPv6SegmentFormat}|:)|` +
+ `(?:${IPv6SegmentFormat}:){5}(?::${IPv4AddressFormat}|(:${IPv6SegmentFormat}){1,2}|:)|` +
+ `(?:${IPv6SegmentFormat}:){4}(?:(:${IPv6SegmentFormat}){0,1}:${IPv4AddressFormat}|(:${IPv6SegmentFormat}){1,3}|:)|` +
+ `(?:${IPv6SegmentFormat}:){3}(?:(:${IPv6SegmentFormat}){0,2}:${IPv4AddressFormat}|(:${IPv6SegmentFormat}){1,4}|:)|` +
+ `(?:${IPv6SegmentFormat}:){2}(?:(:${IPv6SegmentFormat}){0,3}:${IPv4AddressFormat}|(:${IPv6SegmentFormat}){1,5}|:)|` +
+ `(?:${IPv6SegmentFormat}:){1}(?:(:${IPv6SegmentFormat}){0,4}:${IPv4AddressFormat}|(:${IPv6SegmentFormat}){1,6}|:)|` +
+ `(?::((?::${IPv6SegmentFormat}){0,5}:${IPv4AddressFormat}|(?::${IPv6SegmentFormat}){1,7}|:))` +
+ ')(%[0-9a-zA-Z.]{1,})?$');
- if (blocks.length > expectedNumberOfBlocks) {
- return false;
- }
- // initial or final ::
- if (str === '::') {
- return true;
- } else if (str.substr(0, 2) === '::') {
- blocks.shift();
- blocks.shift();
- foundOmissionBlock = true;
- } else if (str.substr(str.length - 2) === '::') {
- blocks.pop();
- blocks.pop();
- foundOmissionBlock = true;
- }
+export default function isIP(ipAddress, options = {}) {
+ assertString(ipAddress);
- for (let i = 0; i < blocks.length; ++i) {
- // test for a :: which can not be at the string start/end
- // since those cases have been handled above
- if (blocks[i] === '' && i > 0 && i < blocks.length - 1) {
- if (foundOmissionBlock) {
- return false; // multiple :: in address
- }
- foundOmissionBlock = true;
- } else if (foundIPv4TransitionBlock && i === blocks.length - 1) {
- // it has been checked before that the last
- // block is a valid IPv4 address
- } else if (!ipv6Block.test(blocks[i])) {
- return false;
- }
- }
- if (foundOmissionBlock) {
- return blocks.length >= 1;
- }
- return blocks.length === expectedNumberOfBlocks;
+ // accessing 'arguments' for backwards compatibility: isIP(ipAddress [, version])
+ // eslint-disable-next-line prefer-rest-params
+ const version = (typeof options === 'object' ? options.version : arguments[1]) || '';
+
+ if (!version) {
+ return isIP(ipAddress, { version: 4 }) || isIP(ipAddress, { version: 6 });
+ }
+
+ if (version.toString() === '4') {
+ return IPv4AddressRegExp.test(ipAddress);
}
+
+ if (version.toString() === '6') {
+ return IPv6AddressRegExp.test(ipAddress);
+ }
+
return false;
}
diff --git a/src/lib/isIPRange.js b/src/lib/isIPRange.js
new file mode 100644
index 000000000..5bb6916c5
--- /dev/null
+++ b/src/lib/isIPRange.js
@@ -0,0 +1,47 @@
+import assertString from './util/assertString';
+import isIP from './isIP';
+
+const subnetMaybe = /^\d{1,3}$/;
+const v4Subnet = 32;
+const v6Subnet = 128;
+
+export default function isIPRange(str, version = '') {
+ assertString(str);
+ const parts = str.split('/');
+
+ // parts[0] -> ip, parts[1] -> subnet
+ if (parts.length !== 2) {
+ return false;
+ }
+
+ if (!subnetMaybe.test(parts[1])) {
+ return false;
+ }
+
+ // Disallow preceding 0 i.e. 01, 02, ...
+ if (parts[1].length > 1 && parts[1].startsWith('0')) {
+ return false;
+ }
+
+ const isValidIP = isIP(parts[0], version);
+ if (!isValidIP) {
+ return false;
+ }
+
+ // Define valid subnet according to IP's version
+ let expectedSubnet = null;
+ switch (String(version)) {
+ case '4':
+ expectedSubnet = v4Subnet;
+ break;
+
+ case '6':
+ expectedSubnet = v6Subnet;
+ break;
+
+ default:
+ expectedSubnet = isIP(parts[0], '6') ? v6Subnet : v4Subnet;
+ }
+
+ return parts[1] <= expectedSubnet && parts[1] >= 0;
+}
diff --git a/src/lib/isISBN.js b/src/lib/isISBN.js
index c66c4c991..4499c59a0 100644
--- a/src/lib/isISBN.js
+++ b/src/lib/isISBN.js
@@ -1,43 +1,55 @@
import assertString from './util/assertString';
-const isbn10Maybe = /^(?:[0-9]{9}X|[0-9]{10})$/;
-const isbn13Maybe = /^(?:[0-9]{13})$/;
+const possibleIsbn10 = /^(?:[0-9]{9}X|[0-9]{10})$/;
+const possibleIsbn13 = /^(?:[0-9]{13})$/;
const factor = [1, 3];
-export default function isISBN(str, version = '') {
- assertString(str);
- version = String(version);
- if (!version) {
- return isISBN(str, 10) || isISBN(str, 13);
+export default function isISBN(isbn, options) {
+ assertString(isbn);
+
+ // For backwards compatibility:
+ // isISBN(str [, version]), i.e. `options` could be used as argument for the legacy `version`
+ const version = String(options?.version || options);
+
+ if (!(options?.version || options)) {
+ return isISBN(isbn, { version: 10 }) || isISBN(isbn, { version: 13 });
}
- const sanitized = str.replace(/[\s-]+/g, '');
+
+ const sanitizedIsbn = isbn.replace(/[\s-]+/g, '');
+
let checksum = 0;
- let i;
+
if (version === '10') {
- if (!isbn10Maybe.test(sanitized)) {
+ if (!possibleIsbn10.test(sanitizedIsbn)) {
return false;
}
- for (i = 0; i < 9; i++) {
- checksum += (i + 1) * sanitized.charAt(i);
+
+ for (let i = 0; i < version - 1; i++) {
+ checksum += (i + 1) * sanitizedIsbn.charAt(i);
}
- if (sanitized.charAt(9) === 'X') {
+
+ if (sanitizedIsbn.charAt(9) === 'X') {
checksum += 10 * 10;
} else {
- checksum += 10 * sanitized.charAt(9);
+ checksum += 10 * sanitizedIsbn.charAt(9);
}
+
if ((checksum % 11) === 0) {
- return !!sanitized;
+ return true;
}
} else if (version === '13') {
- if (!isbn13Maybe.test(sanitized)) {
+ if (!possibleIsbn13.test(sanitizedIsbn)) {
return false;
}
- for (i = 0; i < 12; i++) {
- checksum += factor[i % 2] * sanitized.charAt(i);
+
+ for (let i = 0; i < 12; i++) {
+ checksum += factor[i % 2] * sanitizedIsbn.charAt(i);
}
- if (sanitized.charAt(12) - ((10 - (checksum % 10)) % 10) === 0) {
- return !!sanitized;
+
+ if (sanitizedIsbn.charAt(12) - ((10 - (checksum % 10)) % 10) === 0) {
+ return true;
}
}
+
return false;
}
diff --git a/src/lib/isISIN.js b/src/lib/isISIN.js
index 4576b684c..a6f6fa645 100644
--- a/src/lib/isISIN.js
+++ b/src/lib/isISIN.js
@@ -2,6 +2,12 @@ import assertString from './util/assertString';
const isin = /^[A-Z]{2}[0-9A-Z]{9}[0-9]$/;
+// this link details how the check digit is calculated:
+// https://www.isin.org/isin-format/. it is a little bit
+// odd in that it works with digits, not numbers. in order
+// to make only one pass through the ISIN characters, the
+// each alpha character is handled as 2 characters within
+// the loop.
export default function isISIN(str) {
assertString(str);
@@ -9,27 +15,44 @@ export default function isISIN(str) {
return false;
}
- const checksumStr = str.replace(/[A-Z]/g, character => (parseInt(character, 36)));
-
+ let double = true;
let sum = 0;
- let digit;
- let tmpNum;
- let shouldDouble = true;
- for (let i = checksumStr.length - 2; i >= 0; i--) {
- digit = checksumStr.substring(i, (i + 1));
- tmpNum = parseInt(digit, 10);
- if (shouldDouble) {
- tmpNum *= 2;
- if (tmpNum >= 10) {
- sum += tmpNum + 1;
- } else {
- sum += tmpNum;
+ // convert values
+ for (let i = str.length - 2; i >= 0; i--) {
+ if (str[i] >= 'A' && str[i] <= 'Z') {
+ const value = str[i].charCodeAt(0) - 55;
+ const lo = value % 10;
+ const hi = Math.trunc(value / 10);
+ // letters have two digits, so handle the low order
+ // and high order digits separately.
+ for (const digit of [lo, hi]) {
+ if (double) {
+ if (digit >= 5) {
+ sum += 1 + ((digit - 5) * 2);
+ } else {
+ sum += digit * 2;
+ }
+ } else {
+ sum += digit;
+ }
+ double = !double;
}
} else {
- sum += tmpNum;
+ const digit = str[i].charCodeAt(0) - '0'.charCodeAt(0);
+ if (double) {
+ if (digit >= 5) {
+ sum += 1 + ((digit - 5) * 2);
+ } else {
+ sum += digit * 2;
+ }
+ } else {
+ sum += digit;
+ }
+ double = !double;
}
- shouldDouble = !shouldDouble;
}
- return parseInt(str.substr(str.length - 1), 10) === (10000 - sum) % 10;
+ const check = (Math.trunc(((sum + 9) / 10)) * 10) - sum;
+
+ return +str[str.length - 1] === check;
}
diff --git a/src/lib/isISO15924.js b/src/lib/isISO15924.js
new file mode 100644
index 000000000..30f9e4a4f
--- /dev/null
+++ b/src/lib/isISO15924.js
@@ -0,0 +1,37 @@
+import assertString from './util/assertString';
+
+// from https://www.unicode.org/iso15924/iso15924-codes.html
+const validISO15924Codes = new Set([
+ 'Adlm', 'Afak', 'Aghb', 'Ahom', 'Arab', 'Aran', 'Armi', 'Armn', 'Avst',
+ 'Bali', 'Bamu', 'Bass', 'Batk', 'Beng', 'Bhks', 'Blis', 'Bopo', 'Brah', 'Brai', 'Bugi', 'Buhd',
+ 'Cakm', 'Cans', 'Cari', 'Cham', 'Cher', 'Chis', 'Chrs', 'Cirt', 'Copt', 'Cpmn', 'Cprt', 'Cyrl', 'Cyrs',
+ 'Deva', 'Diak', 'Dogr', 'Dsrt', 'Dupl',
+ 'Egyd', 'Egyh', 'Egyp', 'Elba', 'Elym', 'Ethi',
+ 'Gara', 'Geok', 'Geor', 'Glag', 'Gong', 'Gonm', 'Goth', 'Gran', 'Grek', 'Gujr', 'Gukh', 'Guru',
+ 'Hanb', 'Hang', 'Hani', 'Hano', 'Hans', 'Hant', 'Hatr', 'Hebr', 'Hira', 'Hluw', 'Hmng', 'Hmnp', 'Hrkt', 'Hung',
+ 'Inds', 'Ital',
+ 'Jamo', 'Java', 'Jpan', 'Jurc',
+ 'Kali', 'Kana', 'Kawi', 'Khar', 'Khmr', 'Khoj', 'Kitl', 'Kits', 'Knda', 'Kore', 'Kpel', 'Krai', 'Kthi',
+ 'Lana', 'Laoo', 'Latf', 'Latg', 'Latn', 'Leke', 'Lepc', 'Limb', 'Lina', 'Linb', 'Lisu', 'Loma', 'Lyci', 'Lydi',
+ 'Mahj', 'Maka', 'Mand', 'Mani', 'Marc', 'Maya', 'Medf', 'Mend', 'Merc', 'Mero', 'Mlym', 'Modi', 'Mong', 'Moon', 'Mroo', 'Mtei', 'Mult', 'Mymr',
+ 'Nagm', 'Nand', 'Narb', 'Nbat', 'Newa', 'Nkdb', 'Nkgb', 'Nkoo', 'Nshu',
+ 'Ogam', 'Olck', 'Onao', 'Orkh', 'Orya', 'Osge', 'Osma', 'Ougr',
+ 'Palm', 'Pauc', 'Pcun', 'Pelm', 'Perm', 'Phag', 'Phli', 'Phlp', 'Phlv', 'Phnx', 'Plrd', 'Piqd', 'Prti', 'Psin',
+ 'Qaaa', 'Qaab', 'Qaac', 'Qaad', 'Qaae', 'Qaaf', 'Qaag', 'Qaah', 'Qaai', 'Qaaj', 'Qaak', 'Qaal', 'Qaam', 'Qaan', 'Qaao', 'Qaap', 'Qaaq', 'Qaar', 'Qaas', 'Qaat', 'Qaau', 'Qaav', 'Qaaw', 'Qaax', 'Qaay', 'Qaaz', 'Qaba', 'Qabb', 'Qabc', 'Qabd', 'Qabe', 'Qabf', 'Qabg', 'Qabh', 'Qabi', 'Qabj', 'Qabk', 'Qabl', 'Qabm', 'Qabn', 'Qabo', 'Qabp', 'Qabq', 'Qabr', 'Qabs', 'Qabt', 'Qabu', 'Qabv', 'Qabw', 'Qabx',
+ 'Ranj', 'Rjng', 'Rohg', 'Roro', 'Runr',
+ 'Samr', 'Sara', 'Sarb', 'Saur', 'Sgnw', 'Shaw', 'Shrd', 'Shui', 'Sidd', 'Sidt', 'Sind', 'Sinh', 'Sogd', 'Sogo', 'Sora', 'Soyo', 'Sund', 'Sunu', 'Sylo', 'Syrc', 'Syre', 'Syrj', 'Syrn',
+ 'Tagb', 'Takr', 'Tale', 'Talu', 'Taml', 'Tang', 'Tavt', 'Tayo', 'Telu', 'Teng', 'Tfng', 'Tglg', 'Thaa', 'Thai', 'Tibt', 'Tirh', 'Tnsa', 'Todr', 'Tols', 'Toto', 'Tutg',
+ 'Ugar',
+ 'Vaii', 'Visp', 'Vith',
+ 'Wara', 'Wcho', 'Wole',
+ 'Xpeo', 'Xsux',
+ 'Yezi', 'Yiii',
+ 'Zanb', 'Zinh', 'Zmth', 'Zsye', 'Zsym', 'Zxxx', 'Zyyy', 'Zzzz',
+]);
+
+export default function isISO15924(str) {
+ assertString(str);
+ return validISO15924Codes.has(str);
+}
+
+export const ScriptCodes = validISO15924Codes;
diff --git a/src/lib/isISO31661Alpha2.js b/src/lib/isISO31661Alpha2.js
index af0faf2b6..e67bb1e15 100644
--- a/src/lib/isISO31661Alpha2.js
+++ b/src/lib/isISO31661Alpha2.js
@@ -1,7 +1,7 @@
import assertString from './util/assertString';
// from https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2
-const validISO31661Alpha2CountriesCodes = [
+const validISO31661Alpha2CountriesCodes = new Set([
'AD', 'AE', 'AF', 'AG', 'AI', 'AL', 'AM', 'AO', 'AQ', 'AR', 'AS', 'AT', 'AU', 'AW', 'AX', 'AZ',
'BA', 'BB', 'BD', 'BE', 'BF', 'BG', 'BH', 'BI', 'BJ', 'BL', 'BM', 'BN', 'BO', 'BQ', 'BR', 'BS', 'BT', 'BV', 'BW', 'BY', 'BZ',
'CA', 'CC', 'CD', 'CF', 'CG', 'CH', 'CI', 'CK', 'CL', 'CM', 'CN', 'CO', 'CR', 'CU', 'CV', 'CW', 'CX', 'CY', 'CZ',
@@ -27,9 +27,11 @@ const validISO31661Alpha2CountriesCodes = [
'WF', 'WS',
'YE', 'YT',
'ZA', 'ZM', 'ZW',
-];
+]);
export default function isISO31661Alpha2(str) {
assertString(str);
- return validISO31661Alpha2CountriesCodes.includes(str.toUpperCase());
+ return validISO31661Alpha2CountriesCodes.has(str.toUpperCase());
}
+
+export const CountryCodes = validISO31661Alpha2CountriesCodes;
diff --git a/src/lib/isISO31661Alpha3.js b/src/lib/isISO31661Alpha3.js
new file mode 100644
index 000000000..34e552cdd
--- /dev/null
+++ b/src/lib/isISO31661Alpha3.js
@@ -0,0 +1,26 @@
+import assertString from './util/assertString';
+
+// from https://en.wikipedia.org/wiki/ISO_3166-1_alpha-3
+const validISO31661Alpha3CountriesCodes = new Set([
+ 'AFG', 'ALA', 'ALB', 'DZA', 'ASM', 'AND', 'AGO', 'AIA', 'ATA', 'ATG', 'ARG', 'ARM', 'ABW', 'AUS', 'AUT', 'AZE',
+ 'BHS', 'BHR', 'BGD', 'BRB', 'BLR', 'BEL', 'BLZ', 'BEN', 'BMU', 'BTN', 'BOL', 'BES', 'BIH', 'BWA', 'BVT', 'BRA',
+ 'IOT', 'BRN', 'BGR', 'BFA', 'BDI', 'KHM', 'CMR', 'CAN', 'CPV', 'CYM', 'CAF', 'TCD', 'CHL', 'CHN', 'CXR', 'CCK',
+ 'COL', 'COM', 'COG', 'COD', 'COK', 'CRI', 'CIV', 'HRV', 'CUB', 'CUW', 'CYP', 'CZE', 'DNK', 'DJI', 'DMA', 'DOM',
+ 'ECU', 'EGY', 'SLV', 'GNQ', 'ERI', 'EST', 'ETH', 'FLK', 'FRO', 'FJI', 'FIN', 'FRA', 'GUF', 'PYF', 'ATF', 'GAB',
+ 'GMB', 'GEO', 'DEU', 'GHA', 'GIB', 'GRC', 'GRL', 'GRD', 'GLP', 'GUM', 'GTM', 'GGY', 'GIN', 'GNB', 'GUY', 'HTI',
+ 'HMD', 'VAT', 'HND', 'HKG', 'HUN', 'ISL', 'IND', 'IDN', 'IRN', 'IRQ', 'IRL', 'IMN', 'ISR', 'ITA', 'JAM', 'JPN',
+ 'JEY', 'JOR', 'KAZ', 'KEN', 'KIR', 'PRK', 'KOR', 'KWT', 'KGZ', 'LAO', 'LVA', 'LBN', 'LSO', 'LBR', 'LBY', 'LIE',
+ 'LTU', 'LUX', 'MAC', 'MKD', 'MDG', 'MWI', 'MYS', 'MDV', 'MLI', 'MLT', 'MHL', 'MTQ', 'MRT', 'MUS', 'MYT', 'MEX',
+ 'FSM', 'MDA', 'MCO', 'MNG', 'MNE', 'MSR', 'MAR', 'MOZ', 'MMR', 'NAM', 'NRU', 'NPL', 'NLD', 'NCL', 'NZL', 'NIC',
+ 'NER', 'NGA', 'NIU', 'NFK', 'MNP', 'NOR', 'OMN', 'PAK', 'PLW', 'PSE', 'PAN', 'PNG', 'PRY', 'PER', 'PHL', 'PCN',
+ 'POL', 'PRT', 'PRI', 'QAT', 'REU', 'ROU', 'RUS', 'RWA', 'BLM', 'SHN', 'KNA', 'LCA', 'MAF', 'SPM', 'VCT', 'WSM',
+ 'SMR', 'STP', 'SAU', 'SEN', 'SRB', 'SYC', 'SLE', 'SGP', 'SXM', 'SVK', 'SVN', 'SLB', 'SOM', 'ZAF', 'SGS', 'SSD',
+ 'ESP', 'LKA', 'SDN', 'SUR', 'SJM', 'SWZ', 'SWE', 'CHE', 'SYR', 'TWN', 'TJK', 'TZA', 'THA', 'TLS', 'TGO', 'TKL',
+ 'TON', 'TTO', 'TUN', 'TUR', 'TKM', 'TCA', 'TUV', 'UGA', 'UKR', 'ARE', 'GBR', 'USA', 'UMI', 'URY', 'UZB', 'VUT',
+ 'VEN', 'VNM', 'VGB', 'VIR', 'WLF', 'ESH', 'YEM', 'ZMB', 'ZWE',
+]);
+
+export default function isISO31661Alpha3(str) {
+ assertString(str);
+ return validISO31661Alpha3CountriesCodes.has(str.toUpperCase());
+}
diff --git a/src/lib/isISO31661Numeric.js b/src/lib/isISO31661Numeric.js
new file mode 100644
index 000000000..8b32a8d79
--- /dev/null
+++ b/src/lib/isISO31661Numeric.js
@@ -0,0 +1,26 @@
+import assertString from './util/assertString';
+
+// from https://en.wikipedia.org/wiki/ISO_3166-1_numeric
+const validISO31661NumericCountriesCodes = new Set([
+ '004', '008', '010', '012', '016', '020', '024', '028', '031', '032', '036', '040', '044', '048', '050', '051',
+ '052', '056', '060', '064', '068', '070', '072', '074', '076', '084', '086', '090', '092', '096', '100', '104',
+ '108', '112', '116', '120', '124', '132', '136', '140', '144', '148', '152', '156', '158', '162', '166', '170',
+ '174', '175', '178', '180', '184', '188', '191', '192', '196', '203', '204', '208', '212', '214', '218', '222',
+ '226', '231', '232', '233', '234', '238', '239', '242', '246', '248', '250', '254', '258', '260', '262', '266',
+ '268', '270', '275', '276', '288', '292', '296', '300', '304', '308', '312', '316', '320', '324', '328', '332',
+ '334', '336', '340', '344', '348', '352', '356', '360', '364', '368', '372', '376', '380', '384', '388', '392',
+ '398', '400', '404', '408', '410', '414', '417', '418', '422', '426', '428', '430', '434', '438', '440', '442',
+ '446', '450', '454', '458', '462', '466', '470', '474', '478', '480', '484', '492', '496', '498', '499', '500',
+ '504', '508', '512', '516', '520', '524', '528', '531', '533', '534', '535', '540', '548', '554', '558', '562',
+ '566', '570', '574', '578', '580', '581', '583', '584', '585', '586', '591', '598', '600', '604', '608', '612',
+ '616', '620', '624', '626', '630', '634', '638', '642', '643', '646', '652', '654', '659', '660', '662', '663',
+ '666', '670', '674', '678', '682', '686', '688', '690', '694', '702', '703', '704', '705', '706', '710', '716',
+ '724', '728', '729', '732', '740', '744', '748', '752', '756', '760', '762', '764', '768', '772', '776', '780',
+ '784', '788', '792', '795', '796', '798', '800', '804', '807', '818', '826', '831', '832', '833', '834', '840',
+ '850', '854', '858', '860', '862', '876', '882', '887', '894',
+]);
+
+export default function isISO31661Numeric(str) {
+ assertString(str);
+ return validISO31661NumericCountriesCodes.has(str);
+}
diff --git a/src/lib/isISO4217.js b/src/lib/isISO4217.js
new file mode 100644
index 000000000..bbca596a9
--- /dev/null
+++ b/src/lib/isISO4217.js
@@ -0,0 +1,38 @@
+import assertString from './util/assertString';
+
+// from https://en.wikipedia.org/wiki/ISO_4217
+const validISO4217CurrencyCodes = new Set([
+ 'AED', 'AFN', 'ALL', 'AMD', 'ANG', 'AOA', 'ARS', 'AUD', 'AWG', 'AZN',
+ 'BAM', 'BBD', 'BDT', 'BGN', 'BHD', 'BIF', 'BMD', 'BND', 'BOB', 'BOV', 'BRL', 'BSD', 'BTN', 'BWP', 'BYN', 'BZD',
+ 'CAD', 'CDF', 'CHE', 'CHF', 'CHW', 'CLF', 'CLP', 'CNY', 'COP', 'COU', 'CRC', 'CUP', 'CVE', 'CZK',
+ 'DJF', 'DKK', 'DOP', 'DZD',
+ 'EGP', 'ERN', 'ETB', 'EUR',
+ 'FJD', 'FKP',
+ 'GBP', 'GEL', 'GHS', 'GIP', 'GMD', 'GNF', 'GTQ', 'GYD',
+ 'HKD', 'HNL', 'HTG', 'HUF',
+ 'IDR', 'ILS', 'INR', 'IQD', 'IRR', 'ISK',
+ 'JMD', 'JOD', 'JPY',
+ 'KES', 'KGS', 'KHR', 'KMF', 'KPW', 'KRW', 'KWD', 'KYD', 'KZT',
+ 'LAK', 'LBP', 'LKR', 'LRD', 'LSL', 'LYD',
+ 'MAD', 'MDL', 'MGA', 'MKD', 'MMK', 'MNT', 'MOP', 'MRU', 'MUR', 'MVR', 'MWK', 'MXN', 'MXV', 'MYR', 'MZN',
+ 'NAD', 'NGN', 'NIO', 'NOK', 'NPR', 'NZD',
+ 'OMR',
+ 'PAB', 'PEN', 'PGK', 'PHP', 'PKR', 'PLN', 'PYG',
+ 'QAR',
+ 'RON', 'RSD', 'RUB', 'RWF',
+ 'SAR', 'SBD', 'SCR', 'SDG', 'SEK', 'SGD', 'SHP', 'SLE', 'SLL', 'SOS', 'SRD', 'SSP', 'STN', 'SVC', 'SYP', 'SZL',
+ 'THB', 'TJS', 'TMT', 'TND', 'TOP', 'TRY', 'TTD', 'TWD', 'TZS',
+ 'UAH', 'UGX', 'USD', 'USN', 'UYI', 'UYU', 'UYW', 'UZS',
+ 'VED', 'VES', 'VND', 'VUV',
+ 'WST',
+ 'XAF', 'XAG', 'XAU', 'XBA', 'XBB', 'XBC', 'XBD', 'XCD', 'XDR', 'XOF', 'XPD', 'XPF', 'XPT', 'XSU', 'XTS', 'XUA', 'XXX',
+ 'YER',
+ 'ZAR', 'ZMW', 'ZWL',
+]);
+
+export default function isISO4217(str) {
+ assertString(str);
+ return validISO4217CurrencyCodes.has(str.toUpperCase());
+}
+
+export const CurrencyCodes = validISO4217CurrencyCodes;
diff --git a/src/lib/isISO6346.js b/src/lib/isISO6346.js
new file mode 100644
index 000000000..2c28c1123
--- /dev/null
+++ b/src/lib/isISO6346.js
@@ -0,0 +1,38 @@
+import assertString from './util/assertString';
+
+// https://en.wikipedia.org/wiki/ISO_6346
+// according to ISO6346 standard, checksum digit is mandatory for freight container but recommended
+// for other container types (J and Z)
+const isISO6346Str = /^[A-Z]{3}(U[0-9]{7})|([J,Z][0-9]{6,7})$/;
+const isDigit = /^[0-9]$/;
+
+export function isISO6346(str) {
+ assertString(str);
+
+ str = str.toUpperCase();
+
+ if (!isISO6346Str.test(str)) return false;
+
+ if (str.length === 11) {
+ let sum = 0;
+ for (let i = 0; i < str.length - 1; i++) {
+ if (!isDigit.test(str[i])) {
+ let convertedCode;
+ const letterCode = str.charCodeAt(i) - 55;
+ if (letterCode < 11) convertedCode = letterCode;
+ else if (letterCode >= 11 && letterCode <= 20) convertedCode = 12 + (letterCode % 11);
+ else if (letterCode >= 21 && letterCode <= 30) convertedCode = 23 + (letterCode % 21);
+ else convertedCode = 34 + (letterCode % 31);
+ sum += convertedCode * (2 ** i);
+ } else sum += str[i] * (2 ** i);
+ }
+
+ let checkSumDigit = sum % 11;
+ if (checkSumDigit === 10) checkSumDigit = 0;
+ return Number(str[str.length - 1]) === checkSumDigit;
+ }
+
+ return true;
+}
+
+export const isFreightContainerID = isISO6346;
diff --git a/src/lib/isISO6391.js b/src/lib/isISO6391.js
new file mode 100644
index 000000000..eaa01c5b4
--- /dev/null
+++ b/src/lib/isISO6391.js
@@ -0,0 +1,35 @@
+import assertString from './util/assertString';
+
+const isISO6391Set = new Set([
+ 'aa', 'ab', 'ae', 'af', 'ak', 'am', 'an', 'ar', 'as', 'av', 'ay', 'az', 'az',
+ 'ba', 'be', 'bg', 'bh', 'bi', 'bm', 'bn', 'bo', 'br', 'bs',
+ 'ca', 'ce', 'ch', 'co', 'cr', 'cs', 'cu', 'cv', 'cy',
+ 'da', 'de', 'dv', 'dz',
+ 'ee', 'el', 'en', 'eo', 'es', 'et', 'eu',
+ 'fa', 'ff', 'fi', 'fj', 'fo', 'fr', 'fy',
+ 'ga', 'gd', 'gl', 'gn', 'gu', 'gv',
+ 'ha', 'he', 'hi', 'ho', 'hr', 'ht', 'hu', 'hy', 'hz',
+ 'ia', 'id', 'ie', 'ig', 'ii', 'ik', 'io', 'is', 'it', 'iu',
+ 'ja', 'jv',
+ 'ka', 'kg', 'ki', 'kj', 'kk', 'kl', 'km', 'kn', 'ko', 'kr', 'ks', 'ku', 'kv', 'kw', 'ky',
+ 'la', 'lb', 'lg', 'li', 'ln', 'lo', 'lt', 'lu', 'lv',
+ 'mg', 'mh', 'mi', 'mk', 'ml', 'mn', 'mr', 'ms', 'mt', 'my',
+ 'na', 'nb', 'nd', 'ne', 'ng', 'nl', 'nn', 'no', 'nr', 'nv', 'ny',
+ 'oc', 'oj', 'om', 'or', 'os',
+ 'pa', 'pi', 'pl', 'ps', 'pt',
+ 'qu',
+ 'rm', 'rn', 'ro', 'ru', 'rw',
+ 'sa', 'sc', 'sd', 'se', 'sg', 'si', 'sk', 'sl', 'sm', 'sn', 'so', 'sq', 'sr', 'ss', 'st', 'su', 'sv', 'sw',
+ 'ta', 'te', 'tg', 'th', 'ti', 'tk', 'tl', 'tn', 'to', 'tr', 'ts', 'tt', 'tw', 'ty',
+ 'ug', 'uk', 'ur', 'uz',
+ 've', 'vi', 'vo',
+ 'wa', 'wo',
+ 'xh',
+ 'yi', 'yo',
+ 'za', 'zh', 'zu',
+]);
+
+export default function isISO6391(str) {
+ assertString(str);
+ return isISO6391Set.has(str);
+}
diff --git a/src/lib/isISO8601.js b/src/lib/isISO8601.js
index 7b24e5fe0..1f797347d 100644
--- a/src/lib/isISO8601.js
+++ b/src/lib/isISO8601.js
@@ -2,10 +2,43 @@ import assertString from './util/assertString';
/* eslint-disable max-len */
// from http://goo.gl/0ejHHW
-const iso8601 = /^([\+-]?\d{4}(?!\d{2}\b))((-?)((0[1-9]|1[0-2])(\3([12]\d|0[1-9]|3[01]))?|W([0-4]\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\d|[12]\d{2}|3([0-5]\d|6[1-6])))([T\s]((([01]\d|2[0-3])((:?)[0-5]\d)?|24:?00)([\.,]\d+(?!:))?)?(\17[0-5]\d([\.,]\d+)?)?([zZ]|([\+-])([01]\d|2[0-3]):?([0-5]\d)?)?)?)?$/;
+const iso8601 = /^([\+-]?\d{4}(?!\d{2}\b))((-?)((0[1-9]|1[0-2])(\3([12]\d|0[1-9]|3[01]))?|W([0-4]\d|5[0-3])(-?[1-7])?|(00[1-9]|0[1-9]\d|[12]\d{2}|3([0-5]\d|6[1-6])))([T\s]((([01]\d|2[0-3])((:?)[0-5]\d)?|24:?00)([\.,]\d+(?!:))?)?(\17[0-5]\d([\.,]\d+)?)?([zZ]|([\+-])([01]\d|2[0-3]):?([0-5]\d)?)?)?)?$/;
+// same as above, except with a strict 'T' separator between date and time
+const iso8601StrictSeparator = /^([\+-]?\d{4}(?!\d{2}\b))((-?)((0[1-9]|1[0-2])(\3([12]\d|0[1-9]|3[01]))?|W([0-4]\d|5[0-3])(-?[1-7])?|(00[1-9]|0[1-9]\d|[12]\d{2}|3([0-5]\d|6[1-6])))([T]((([01]\d|2[0-3])((:?)[0-5]\d)?|24:?00)([\.,]\d+(?!:))?)?(\17[0-5]\d([\.,]\d+)?)?([zZ]|([\+-])([01]\d|2[0-3]):?([0-5]\d)?)?)?)?$/;
/* eslint-enable max-len */
+const isValidDate = (str) => {
+ // str must have passed the ISO8601 check
+ // this check is meant to catch invalid dates
+ // like 2009-02-31
+ // first check for ordinal dates
+ const ordinalMatch = str.match(/^(\d{4})-?(\d{3})([ T]{1}\.*|$)/);
+ if (ordinalMatch) {
+ const oYear = Number(ordinalMatch[1]);
+ const oDay = Number(ordinalMatch[2]);
+ // if is leap year
+ if ((oYear % 4 === 0 && oYear % 100 !== 0) || oYear % 400 === 0) return oDay <= 366;
+ return oDay <= 365;
+ }
+ const match = str.match(/(\d{4})-?(\d{0,2})-?(\d*)/).map(Number);
+ const year = match[1];
+ const month = match[2];
+ const day = match[3];
+ const monthString = month ? `0${month}`.slice(-2) : month;
+ const dayString = day ? `0${day}`.slice(-2) : day;
-export default function isISO8601(str) {
+ // create a date object and compare
+ const d = new Date(`${year}-${monthString || '01'}-${dayString || '01'}`);
+ if (month && day) {
+ return d.getUTCFullYear() === year
+ && (d.getUTCMonth() + 1) === month
+ && d.getUTCDate() === day;
+ }
+ return true;
+};
+
+export default function isISO8601(str, options = {}) {
assertString(str);
- return iso8601.test(str);
+ const check = options.strictSeparator ? iso8601StrictSeparator.test(str) : iso8601.test(str);
+ if (check && options.strict) return isValidDate(str);
+ return check;
}
diff --git a/src/lib/isISSN.js b/src/lib/isISSN.js
index 967233e43..42b9ad7eb 100644
--- a/src/lib/isISSN.js
+++ b/src/lib/isISSN.js
@@ -10,13 +10,11 @@ export default function isISSN(str, options = {}) {
if (!testIssn.test(str)) {
return false;
}
- const issnDigits = str.replace('-', '');
- let position = 8;
+ const digits = str.replace('-', '').toUpperCase();
let checksum = 0;
- for (const digit of issnDigits) {
- const digitValue = digit.toUpperCase() === 'X' ? 10 : +digit;
- checksum += digitValue * position;
- --position;
+ for (let i = 0; i < digits.length; i++) {
+ const digit = digits[i];
+ checksum += (digit === 'X' ? 10 : +digit) * (8 - i);
}
return checksum % 11 === 0;
}
diff --git a/src/lib/isIdentityCard.js b/src/lib/isIdentityCard.js
new file mode 100644
index 000000000..fc37d4a29
--- /dev/null
+++ b/src/lib/isIdentityCard.js
@@ -0,0 +1,455 @@
+import assertString from './util/assertString';
+import includes from './util/includesArray';
+import isInt from './isInt';
+
+const validators = {
+ PL: (str) => {
+ assertString(str);
+
+ const weightOfDigits = {
+ 1: 1,
+ 2: 3,
+ 3: 7,
+ 4: 9,
+ 5: 1,
+ 6: 3,
+ 7: 7,
+ 8: 9,
+ 9: 1,
+ 10: 3,
+ 11: 0,
+ };
+
+ if (str != null && str.length === 11 && isInt(str, { allow_leading_zeroes: true })) {
+ const digits = str.split('').slice(0, -1);
+ const sum = digits.reduce((acc, digit, index) =>
+ acc + (Number(digit) * weightOfDigits[index + 1]), 0);
+
+ const modulo = sum % 10;
+ const lastDigit = Number(str.charAt(str.length - 1));
+
+ if ((modulo === 0 && lastDigit === 0) || lastDigit === 10 - modulo) {
+ return true;
+ }
+ }
+
+ return false;
+ },
+ ES: (str) => {
+ assertString(str);
+
+ const DNI = /^[0-9X-Z][0-9]{7}[TRWAGMYFPDXBNJZSQVHLCKE]$/;
+
+ const charsValue = {
+ X: 0,
+ Y: 1,
+ Z: 2,
+ };
+
+ const controlDigits = [
+ 'T', 'R', 'W', 'A', 'G', 'M', 'Y', 'F', 'P', 'D', 'X', 'B',
+ 'N', 'J', 'Z', 'S', 'Q', 'V', 'H', 'L', 'C', 'K', 'E',
+ ];
+
+ // sanitize user input
+ const sanitized = str.trim().toUpperCase();
+
+ // validate the data structure
+ if (!DNI.test(sanitized)) {
+ return false;
+ }
+
+ // validate the control digit
+ const number = sanitized.slice(0, -1).replace(/[X,Y,Z]/g, char => charsValue[char]);
+
+ return sanitized.endsWith(controlDigits[number % 23]);
+ },
+ FI: (str) => {
+ // https://dvv.fi/en/personal-identity-code#:~:text=control%20character%20for%20a-,personal,-identity%20code%20calculated
+ assertString(str);
+
+ if (str.length !== 11) {
+ return false;
+ }
+
+ if (!str.match(/^\d{6}[\-A\+]\d{3}[0-9ABCDEFHJKLMNPRSTUVWXY]{1}$/)) {
+ return false;
+ }
+
+ const checkDigits = '0123456789ABCDEFHJKLMNPRSTUVWXY';
+
+ const idAsNumber = (parseInt(str.slice(0, 6), 10) * 1000) + parseInt(str.slice(7, 10), 10);
+ const remainder = idAsNumber % 31;
+ const checkDigit = checkDigits[remainder];
+
+ return checkDigit === str.slice(10, 11);
+ },
+ IN: (str) => {
+ const DNI = /^[1-9]\d{3}\s?\d{4}\s?\d{4}$/;
+
+ // multiplication table
+ const d = [
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
+ [1, 2, 3, 4, 0, 6, 7, 8, 9, 5],
+ [2, 3, 4, 0, 1, 7, 8, 9, 5, 6],
+ [3, 4, 0, 1, 2, 8, 9, 5, 6, 7],
+ [4, 0, 1, 2, 3, 9, 5, 6, 7, 8],
+ [5, 9, 8, 7, 6, 0, 4, 3, 2, 1],
+ [6, 5, 9, 8, 7, 1, 0, 4, 3, 2],
+ [7, 6, 5, 9, 8, 2, 1, 0, 4, 3],
+ [8, 7, 6, 5, 9, 3, 2, 1, 0, 4],
+ [9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
+ ];
+
+ // permutation table
+ const p = [
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
+ [1, 5, 7, 6, 2, 8, 3, 0, 9, 4],
+ [5, 8, 0, 3, 7, 9, 6, 1, 4, 2],
+ [8, 9, 1, 6, 0, 4, 3, 5, 2, 7],
+ [9, 4, 5, 3, 1, 2, 6, 8, 7, 0],
+ [4, 2, 8, 6, 5, 7, 3, 9, 0, 1],
+ [2, 7, 9, 3, 8, 0, 6, 4, 1, 5],
+ [7, 0, 4, 6, 9, 1, 3, 2, 5, 8],
+ ];
+
+ // sanitize user input
+ const sanitized = str.trim();
+
+ // validate the data structure
+ if (!DNI.test(sanitized)) {
+ return false;
+ }
+ let c = 0;
+ let invertedArray = sanitized.replace(/\s/g, '').split('').map(Number).reverse();
+
+ invertedArray.forEach((val, i) => {
+ c = d[c][p[(i % 8)][val]];
+ });
+
+ return c === 0;
+ },
+ IR: (str) => {
+ if (!str.match(/^\d{10}$/)) return false;
+ str = (`0000${str}`).slice(str.length - 6);
+
+ if (parseInt(str.slice(3, 9), 10) === 0) return false;
+
+ const lastNumber = parseInt(str.slice(9, 10), 10);
+ let sum = 0;
+
+ for (let i = 0; i < 9; i++) {
+ sum += parseInt(str.slice(i, i + 1), 10) * (10 - i);
+ }
+
+ sum %= 11;
+
+ return (
+ (sum < 2 && lastNumber === sum) || (sum >= 2 && lastNumber === 11 - sum)
+ );
+ },
+ IT: function IT(str) {
+ if (str.length !== 9) return false;
+ if (str === 'CA00000AA') return false; // https://it.wikipedia.org/wiki/Carta_d%27identit%C3%A0_elettronica_italiana
+ return str.search(/C[A-Z]\d{5}[A-Z]{2}/is) > -1;
+ },
+ NO: (str) => {
+ const sanitized = str.trim();
+ if (isNaN(Number(sanitized))) return false;
+ if (sanitized.length !== 11) return false;
+ if (sanitized === '00000000000') return false;
+
+ // https://no.wikipedia.org/wiki/F%C3%B8dselsnummer
+ const f = sanitized.split('').map(Number);
+ let k1 = (11 - (((3 * f[0]) + (7 * f[1]) + (6 * f[2])
+ + (1 * f[3]) + (8 * f[4]) + (9 * f[5]) + (4 * f[6])
+ + (5 * f[7]) + (2 * f[8])) % 11)) % 11;
+ let k2 = (11 - (((5 * f[0]) + (4 * f[1]) + (3 * f[2])
+ + (2 * f[3]) + (7 * f[4]) + (6 * f[5]) + (5 * f[6])
+ + (4 * f[7]) + (3 * f[8]) + (2 * k1)) % 11)) % 11;
+
+ if (k1 !== f[9] || k2 !== f[10]) return false;
+ return true;
+ },
+ TH: (str) => {
+ if (!str.match(/^[1-8]\d{12}$/)) return false;
+
+ // validate check digit
+ let sum = 0;
+ for (let i = 0; i < 12; i++) {
+ sum += parseInt(str[i], 10) * (13 - i);
+ }
+ return str[12] === ((11 - (sum % 11)) % 10).toString();
+ },
+ LK: (str) => {
+ const old_nic = /^[1-9]\d{8}[vx]$/i;
+ const new_nic = /^[1-9]\d{11}$/i;
+
+ if (str.length === 10 && old_nic.test(str)) return true;
+ else if (str.length === 12 && new_nic.test(str)) return true;
+ return false;
+ },
+ 'he-IL': (str) => {
+ const DNI = /^\d{9}$/;
+
+ // sanitize user input
+ const sanitized = str.trim();
+
+ // validate the data structure
+ if (!DNI.test(sanitized)) {
+ return false;
+ }
+
+ const id = sanitized;
+
+ let sum = 0,
+ incNum;
+ for (let i = 0; i < id.length; i++) {
+ incNum = Number(id[i]) * ((i % 2) + 1); // Multiply number by 1 or 2
+ sum += incNum > 9 ? incNum - 9 : incNum; // Sum the digits up and add to total
+ }
+ return sum % 10 === 0;
+ },
+ 'ar-LY': (str) => {
+ // Libya National Identity Number NIN is 12 digits, the first digit is either 1 or 2
+ const NIN = /^(1|2)\d{11}$/;
+
+ // sanitize user input
+ const sanitized = str.trim();
+
+ // validate the data structure
+ if (!NIN.test(sanitized)) {
+ return false;
+ }
+ return true;
+ },
+ 'ar-TN': (str) => {
+ const DNI = /^\d{8}$/;
+
+ // sanitize user input
+ const sanitized = str.trim();
+
+ // validate the data structure
+ if (!DNI.test(sanitized)) {
+ return false;
+ }
+ return true;
+ },
+ 'zh-CN': (str) => {
+ const provincesAndCities = [
+ '11', // 北京
+ '12', // 天津
+ '13', // 河北
+ '14', // 山西
+ '15', // 内蒙古
+ '21', // 辽宁
+ '22', // 吉林
+ '23', // 黑龙江
+ '31', // 上海
+ '32', // 江苏
+ '33', // 浙江
+ '34', // 安徽
+ '35', // 福建
+ '36', // 江西
+ '37', // 山东
+ '41', // 河南
+ '42', // 湖北
+ '43', // 湖南
+ '44', // 广东
+ '45', // 广西
+ '46', // 海南
+ '50', // 重庆
+ '51', // 四川
+ '52', // 贵州
+ '53', // 云南
+ '54', // 西藏
+ '61', // 陕西
+ '62', // 甘肃
+ '63', // 青海
+ '64', // 宁夏
+ '65', // 新疆
+ '71', // 台湾
+ '81', // 香港
+ '82', // 澳门
+ '91', // 国外
+ ];
+
+ const powers = ['7', '9', '10', '5', '8', '4', '2', '1', '6', '3', '7', '9', '10', '5', '8', '4', '2'];
+
+ const parityBit = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'];
+
+ const checkAddressCode = addressCode => includes(provincesAndCities, addressCode);
+
+ const checkBirthDayCode = (birDayCode) => {
+ const yyyy = parseInt(birDayCode.substring(0, 4), 10);
+ const mm = parseInt(birDayCode.substring(4, 6), 10);
+ const dd = parseInt(birDayCode.substring(6), 10);
+ const xdata = new Date(yyyy, mm - 1, dd);
+ if (xdata > new Date()) {
+ return false;
+ // eslint-disable-next-line max-len
+ } else if ((xdata.getFullYear() === yyyy) && (xdata.getMonth() === mm - 1) && (xdata.getDate() === dd)) {
+ return true;
+ }
+ return false;
+ };
+
+ const getParityBit = (idCardNo) => {
+ let id17 = idCardNo.substring(0, 17);
+
+ let power = 0;
+ for (let i = 0; i < 17; i++) {
+ power += parseInt(id17.charAt(i), 10) * parseInt(powers[i], 10);
+ }
+
+ let mod = power % 11;
+ return parityBit[mod];
+ };
+
+ const checkParityBit = idCardNo => getParityBit(idCardNo) === idCardNo.charAt(17).toUpperCase();
+
+
+ const check15IdCardNo = (idCardNo) => {
+ let check = /^[1-9]\d{7}((0[1-9])|(1[0-2]))((0[1-9])|([1-2][0-9])|(3[0-1]))\d{3}$/.test(idCardNo);
+ if (!check) return false;
+ let addressCode = idCardNo.substring(0, 2);
+ check = checkAddressCode(addressCode);
+ if (!check) return false;
+ let birDayCode = `19${idCardNo.substring(6, 12)}`;
+ check = checkBirthDayCode(birDayCode);
+ if (!check) return false;
+ return true;
+ };
+
+ const check18IdCardNo = (idCardNo) => {
+ let check = /^[1-9]\d{5}[1-9]\d{3}((0[1-9])|(1[0-2]))((0[1-9])|([1-2][0-9])|(3[0-1]))\d{3}(\d|x|X)$/.test(idCardNo);
+ if (!check) return false;
+ let addressCode = idCardNo.substring(0, 2);
+ check = checkAddressCode(addressCode);
+ if (!check) return false;
+ let birDayCode = idCardNo.substring(6, 14);
+ check = checkBirthDayCode(birDayCode);
+ if (!check) return false;
+ return checkParityBit(idCardNo);
+ };
+
+ const checkIdCardNo = (idCardNo) => {
+ let check = /^\d{15}|(\d{17}(\d|x|X))$/.test(idCardNo);
+ if (!check) return false;
+ if (idCardNo.length === 15) {
+ return check15IdCardNo(idCardNo);
+ }
+ return check18IdCardNo(idCardNo);
+ };
+ return checkIdCardNo(str);
+ },
+ 'zh-HK': (str) => {
+ // sanitize user input
+ str = str.trim();
+
+ // HKID number starts with 1 or 2 letters, followed by 6 digits,
+ // then a checksum contained in square / round brackets or nothing
+ const regexHKID = /^[A-Z]{1,2}[0-9]{6}((\([0-9A]\))|(\[[0-9A]\])|([0-9A]))$/;
+ const regexIsDigit = /^[0-9]$/;
+
+ // convert the user input to all uppercase and apply regex
+ str = str.toUpperCase();
+ if (!regexHKID.test(str)) return false;
+ str = str.replace(/\[|\]|\(|\)/g, '');
+
+ if (str.length === 8) str = `3${str}`;
+ let checkSumVal = 0;
+ for (let i = 0; i <= 7; i++) {
+ let convertedChar;
+ if (!regexIsDigit.test(str[i])) convertedChar = (str[i].charCodeAt(0) - 55) % 11;
+ else convertedChar = str[i];
+ checkSumVal += (convertedChar * (9 - i));
+ }
+ checkSumVal %= 11;
+
+ let checkSumConverted;
+ if (checkSumVal === 0) checkSumConverted = '0';
+ else if (checkSumVal === 1) checkSumConverted = 'A';
+ else checkSumConverted = String(11 - checkSumVal);
+ if (checkSumConverted === str[str.length - 1]) return true;
+ return false;
+ },
+ 'zh-TW': (str) => {
+ const ALPHABET_CODES = {
+ A: 10,
+ B: 11,
+ C: 12,
+ D: 13,
+ E: 14,
+ F: 15,
+ G: 16,
+ H: 17,
+ I: 34,
+ J: 18,
+ K: 19,
+ L: 20,
+ M: 21,
+ N: 22,
+ O: 35,
+ P: 23,
+ Q: 24,
+ R: 25,
+ S: 26,
+ T: 27,
+ U: 28,
+ V: 29,
+ W: 32,
+ X: 30,
+ Y: 31,
+ Z: 33,
+ };
+
+ const sanitized = str.trim().toUpperCase();
+
+ if (!/^[A-Z][0-9]{9}$/.test(sanitized)) return false;
+
+ return Array.from(sanitized).reduce((sum, number, index) => {
+ if (index === 0) {
+ const code = ALPHABET_CODES[number];
+
+ return ((code % 10) * 9) + Math.floor(code / 10);
+ }
+
+ if (index === 9) {
+ return ((10 - (sum % 10)) - Number(number)) % 10 === 0;
+ }
+
+ return sum + (Number(number) * (9 - index));
+ }, 0);
+ },
+ PK: (str) => {
+ // Pakistani National Identity Number CNIC is 13 digits
+ const CNIC = /^[1-7][0-9]{4}-[0-9]{7}-[1-9]$/;
+
+ // sanitize user input
+ const sanitized = str.trim();
+
+ // validate the data structure
+ return CNIC.test(sanitized);
+ },
+};
+
+export default function isIdentityCard(str, locale) {
+ assertString(str);
+ if (locale in validators) {
+ return validators[locale](str);
+ } else if (locale === 'any') {
+ for (const key in validators) {
+ // https://github.com/gotwarlost/istanbul/blob/master/ignoring-code-for-coverage.md#ignoring-code-for-coverage-purposes
+ // istanbul ignore else
+ if (validators.hasOwnProperty(key)) {
+ const validator = validators[key];
+ if (validator(str)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ throw new Error(`Invalid locale '${locale}'`);
+}
diff --git a/src/lib/isIn.js b/src/lib/isIn.js
index 2b00355ef..e7f15804f 100644
--- a/src/lib/isIn.js
+++ b/src/lib/isIn.js
@@ -7,6 +7,8 @@ export default function isIn(str, options) {
if (Object.prototype.toString.call(options) === '[object Array]') {
const array = [];
for (i in options) {
+ // https://github.com/gotwarlost/istanbul/blob/master/ignoring-code-for-coverage.md#ignoring-code-for-coverage-purposes
+ // istanbul ignore else
if ({}.hasOwnProperty.call(options, i)) {
array[i] = toString(options[i]);
}
diff --git a/src/lib/isInt.js b/src/lib/isInt.js
index 8047a6969..d5616089a 100644
--- a/src/lib/isInt.js
+++ b/src/lib/isInt.js
@@ -1,4 +1,5 @@
import assertString from './util/assertString';
+import isNullOrUndefined from './util/nullUndefinedCheck';
const int = /^(?:[-+]?(?:0|[1-9][0-9]*))$/;
const intLeadingZeroes = /^[-+]?[0-9]+$/;
@@ -9,16 +10,13 @@ export default function isInt(str, options) {
// Get the regex to use for testing, based on whether
// leading zeroes are allowed or not.
- let regex = (
- options.hasOwnProperty('allow_leading_zeroes') && !options.allow_leading_zeroes ?
- int : intLeadingZeroes
- );
+ const regex = options.allow_leading_zeroes === false ? int : intLeadingZeroes;
// Check min/max/lt/gt
- let minCheckPassed = (!options.hasOwnProperty('min') || str >= options.min);
- let maxCheckPassed = (!options.hasOwnProperty('max') || str <= options.max);
- let ltCheckPassed = (!options.hasOwnProperty('lt') || str < options.lt);
- let gtCheckPassed = (!options.hasOwnProperty('gt') || str > options.gt);
+ let minCheckPassed = (!options.hasOwnProperty('min') || isNullOrUndefined(options.min) || str >= options.min);
+ let maxCheckPassed = (!options.hasOwnProperty('max') || isNullOrUndefined(options.max) || str <= options.max);
+ let ltCheckPassed = (!options.hasOwnProperty('lt') || isNullOrUndefined(options.lt) || str < options.lt);
+ let gtCheckPassed = (!options.hasOwnProperty('gt') || isNullOrUndefined(options.gt) || str > options.gt);
return regex.test(str) && minCheckPassed && maxCheckPassed && ltCheckPassed && gtCheckPassed;
}
diff --git a/src/lib/isJSON.js b/src/lib/isJSON.js
index ac434839e..5c51dd31c 100644
--- a/src/lib/isJSON.js
+++ b/src/lib/isJSON.js
@@ -1,10 +1,22 @@
import assertString from './util/assertString';
+import includes from './util/includesArray';
+import merge from './util/merge';
-export default function isJSON(str) {
+const default_json_options = {
+ allow_primitives: false,
+};
+
+export default function isJSON(str, options) {
assertString(str);
try {
+ options = merge(options, default_json_options);
+ let primitives = [];
+ if (options.allow_primitives) {
+ primitives = [null, false, true];
+ }
+
const obj = JSON.parse(str);
- return !!obj && typeof obj === 'object';
+ return includes(primitives, obj) || (!!obj && typeof obj === 'object');
} catch (e) { /* ignore */ }
return false;
}
diff --git a/src/lib/isJWT.js b/src/lib/isJWT.js
new file mode 100644
index 000000000..1d0ade5ee
--- /dev/null
+++ b/src/lib/isJWT.js
@@ -0,0 +1,15 @@
+import assertString from './util/assertString';
+import isBase64 from './isBase64';
+
+export default function isJWT(str) {
+ assertString(str);
+
+ const dotSplit = str.split('.');
+ const len = dotSplit.length;
+
+ if (len !== 3) {
+ return false;
+ }
+
+ return dotSplit.reduce((acc, currElem) => acc && isBase64(currElem, { urlSafe: true }), true);
+}
diff --git a/src/lib/isLatLong.js b/src/lib/isLatLong.js
index ec401575e..c4e622753 100644
--- a/src/lib/isLatLong.js
+++ b/src/lib/isLatLong.js
@@ -1,11 +1,28 @@
import assertString from './util/assertString';
+import merge from './util/merge';
+import includes from './util/includesString';
const lat = /^\(?[+-]?(90(\.0+)?|[1-8]?\d(\.\d+)?)$/;
const long = /^\s?[+-]?(180(\.0+)?|1[0-7]\d(\.\d+)?|\d{1,2}(\.\d+)?)\)?$/;
-export default function (str) {
+const latDMS = /^(([1-8]?\d)\D+([1-5]?\d|60)\D+([1-5]?\d|60)(\.\d+)?|90\D+0\D+0)\D+[NSns]?$/i;
+const longDMS = /^\s*([1-7]?\d{1,2}\D+([1-5]?\d|60)\D+([1-5]?\d|60)(\.\d+)?|180\D+0\D+0)\D+[EWew]?$/i;
+
+const defaultLatLongOptions = {
+ checkDMS: false,
+};
+
+export default function isLatLong(str, options) {
assertString(str);
- if (!str.includes(',')) return false;
+ options = merge(options, defaultLatLongOptions);
+
+ if (!includes(str, ',')) return false;
const pair = str.split(',');
+ if ((pair[0].startsWith('(') && !pair[1].endsWith(')'))
+ || (pair[1].endsWith(')') && !pair[0].startsWith('('))) return false;
+
+ if (options.checkDMS) {
+ return latDMS.test(pair[0]) && longDMS.test(pair[1]);
+ }
return lat.test(pair[0]) && long.test(pair[1]);
}
diff --git a/src/lib/isLength.js b/src/lib/isLength.js
index 67e1ece11..56d8e3fbf 100644
--- a/src/lib/isLength.js
+++ b/src/lib/isLength.js
@@ -5,14 +5,23 @@ export default function isLength(str, options) {
assertString(str);
let min;
let max;
+
if (typeof (options) === 'object') {
min = options.min || 0;
max = options.max;
} else { // backwards compatibility: isLength(str, min [, max])
- min = arguments[1];
+ min = arguments[1] || 0;
max = arguments[2];
}
+
+ const presentationSequences = str.match(/[^\uFE0F\uFE0E][\uFE0F\uFE0E]/g) || [];
const surrogatePairs = str.match(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g) || [];
- const len = str.length - surrogatePairs.length;
- return len >= min && (typeof max === 'undefined' || len <= max);
+ const len = str.length - presentationSequences.length - surrogatePairs.length;
+ const isInsideRange = len >= min && (typeof max === 'undefined' || len <= max);
+
+ if (isInsideRange && Array.isArray(options?.discreteLengths)) {
+ return options.discreteLengths.some(discreteLen => discreteLen === len);
+ }
+
+ return isInsideRange;
}
diff --git a/src/lib/isLicensePlate.js b/src/lib/isLicensePlate.js
new file mode 100644
index 000000000..54d80635f
--- /dev/null
+++ b/src/lib/isLicensePlate.js
@@ -0,0 +1,40 @@
+import assertString from './util/assertString';
+
+const validators = {
+ 'cs-CZ': str =>
+ /^(([ABCDEFHIJKLMNPRSTUVXYZ]|[0-9])-?){5,8}$/.test(str),
+ 'de-DE': str =>
+ /^((A|AA|AB|AC|AE|AH|AK|AM|AN|AÖ|AP|AS|AT|AU|AW|AZ|B|BA|BB|BC|BE|BF|BH|BI|BK|BL|BM|BN|BO|BÖ|BS|BT|BZ|C|CA|CB|CE|CO|CR|CW|D|DA|DD|DE|DH|DI|DL|DM|DN|DO|DU|DW|DZ|E|EA|EB|ED|EE|EF|EG|EH|EI|EL|EM|EN|ER|ES|EU|EW|F|FB|FD|FF|FG|FI|FL|FN|FO|FR|FS|FT|FÜ|FW|FZ|G|GA|GC|GD|GE|GF|GG|GI|GK|GL|GM|GN|GÖ|GP|GR|GS|GT|GÜ|GV|GW|GZ|H|HA|HB|HC|HD|HE|HF|HG|HH|HI|HK|HL|HM|HN|HO|HP|HR|HS|HU|HV|HX|HY|HZ|IK|IL|IN|IZ|J|JE|JL|K|KA|KB|KC|KE|KF|KG|KH|KI|KK|KL|KM|KN|KO|KR|KS|KT|KU|KW|KY|L|LA|LB|LC|LD|LF|LG|LH|LI|LL|LM|LN|LÖ|LP|LR|LU|M|MA|MB|MC|MD|ME|MG|MH|MI|MK|ML|MM|MN|MO|MQ|MR|MS|MÜ|MW|MY|MZ|N|NB|ND|NE|NF|NH|NI|NK|NM|NÖ|NP|NR|NT|NU|NW|NY|NZ|OA|OB|OC|OD|OE|OF|OG|OH|OK|OL|OP|OS|OZ|P|PA|PB|PE|PF|PI|PL|PM|PN|PR|PS|PW|PZ|R|RA|RC|RD|RE|RG|RH|RI|RL|RM|RN|RO|RP|RS|RT|RU|RV|RW|RZ|S|SB|SC|SE|SG|SI|SK|SL|SM|SN|SO|SP|SR|ST|SU|SW|SY|SZ|TE|TF|TG|TO|TP|TR|TS|TT|TÜ|ÜB|UE|UH|UL|UM|UN|V|VB|VG|VK|VR|VS|W|WA|WB|WE|WF|WI|WK|WL|WM|WN|WO|WR|WS|WT|WÜ|WW|WZ|Z|ZE|ZI|ZP|ZR|ZW|ZZ)[- ]?[A-Z]{1,2}[- ]?\d{1,4}|(ABG|ABI|AIB|AIC|ALF|ALZ|ANA|ANG|ANK|APD|ARN|ART|ASL|ASZ|AUR|AZE|BAD|BAR|BBG|BCH|BED|BER|BGD|BGL|BID|BIN|BIR|BIT|BIW|BKS|BLB|BLK|BNA|BOG|BOH|BOR|BOT|BRA|BRB|BRG|BRK|BRL|BRV|BSB|BSK|BTF|BÜD|BUL|BÜR|BÜS|BÜZ|CAS|CHA|CLP|CLZ|COC|COE|CUX|DAH|DAN|DAU|DBR|DEG|DEL|DGF|DIL|DIN|DIZ|DKB|DLG|DON|DUD|DÜW|EBE|EBN|EBS|ECK|EIC|EIL|EIN|EIS|EMD|EMS|ERB|ERH|ERK|ERZ|ESB|ESW|FDB|FDS|FEU|FFB|FKB|FLÖ|FOR|FRG|FRI|FRW|FTL|FÜS|GAN|GAP|GDB|GEL|GEO|GER|GHA|GHC|GLA|GMN|GNT|GOA|GOH|GRA|GRH|GRI|GRM|GRZ|GTH|GUB|GUN|GVM|HAB|HAL|HAM|HAS|HBN|HBS|HCH|HDH|HDL|HEB|HEF|HEI|HER|HET|HGN|HGW|HHM|HIG|HIP|HMÜ|HOG|HOH|HOL|HOM|HOR|HÖS|HOT|HRO|HSK|HST|HVL|HWI|IGB|ILL|JÜL|KEH|KEL|KEM|KIB|KLE|KLZ|KÖN|KÖT|KÖZ|KRU|KÜN|KUS|KYF|LAN|LAU|LBS|LBZ|LDK|LDS|LEO|LER|LEV|LIB|LIF|LIP|LÖB|LOS|LRO|LSZ|LÜN|LUP|LWL|MAB|MAI|MAK|MAL|MED|MEG|MEI|MEK|MEL|MER|MET|MGH|MGN|MHL|MIL|MKK|MOD|MOL|MON|MOS|MSE|MSH|MSP|MST|MTK|MTL|MÜB|MÜR|MYK|MZG|NAB|NAI|NAU|NDH|NEA|NEB|NEC|NEN|NES|NEW|NMB|NMS|NOH|NOL|NOM|NOR|NVP|NWM|OAL|OBB|OBG|OCH|OHA|ÖHR|OHV|OHZ|OPR|OSL|OVI|OVL|OVP|PAF|PAN|PAR|PCH|PEG|PIR|PLÖ|PRÜ|QFT|QLB|RDG|REG|REH|REI|RID|RIE|ROD|ROF|ROK|ROL|ROS|ROT|ROW|RSL|RÜD|RÜG|SAB|SAD|SAN|SAW|SBG|SBK|SCZ|SDH|SDL|SDT|SEB|SEE|SEF|SEL|SFB|SFT|SGH|SHA|SHG|SHK|SHL|SIG|SIM|SLE|SLF|SLK|SLN|SLS|SLÜ|SLZ|SMÜ|SOB|SOG|SOK|SÖM|SON|SPB|SPN|SRB|SRO|STA|STB|STD|STE|STL|SUL|SÜW|SWA|SZB|TBB|TDO|TET|TIR|TÖL|TUT|UEM|UER|UFF|USI|VAI|VEC|VER|VIB|VIE|VIT|VOH|WAF|WAK|WAN|WAR|WAT|WBS|WDA|WEL|WEN|WER|WES|WHV|WIL|WIS|WIT|WIZ|WLG|WMS|WND|WOB|WOH|WOL|WOR|WOS|WRN|WSF|WST|WSW|WTL|WTM|WUG|WÜM|WUN|WUR|WZL|ZEL|ZIG)[- ]?(([A-Z][- ]?\d{1,4})|([A-Z]{2}[- ]?\d{1,3})))[- ]?(E|H)?$/.test(str),
+ 'de-LI': str => /^FL[- ]?\d{1,5}[UZ]?$/.test(str),
+ 'en-IN': str => /^[A-Z]{2}[ -]?[0-9]{1,2}(?:[ -]?[A-Z])(?:[ -]?[A-Z]*)?[ -]?[0-9]{4}$/.test(str),
+ 'en-SG': str => /^[A-Z]{3}[ -]?[\d]{4}[ -]?[A-Z]{1}$/.test(str),
+ 'es-AR': str => /^(([A-Z]{2} ?[0-9]{3} ?[A-Z]{2})|([A-Z]{3} ?[0-9]{3}))$/.test(str),
+ 'fi-FI': str => /^(?=.{4,7})(([A-Z]{1,3}|[0-9]{1,3})[\s-]?([A-Z]{1,3}|[0-9]{1,5}))$/.test(str),
+ 'hu-HU': str => /^((((?!AAA)(([A-NPRSTVZWXY]{1})([A-PR-Z]{1})([A-HJ-NPR-Z]))|(A[ABC]I)|A[ABC]O|A[A-W]Q|BPI|BPO|UCO|UDO|XAO)-(?!000)\d{3})|(M\d{6})|((CK|DT|CD|HC|H[ABEFIKLMNPRSTVX]|MA|OT|R[A-Z]) \d{2}-\d{2})|(CD \d{3}-\d{3})|(C-(C|X) \d{4})|(X-(A|B|C) \d{4})|(([EPVZ]-\d{5}))|(S A[A-Z]{2} \d{2})|(SP \d{2}-\d{2}))$/.test(str),
+ 'pt-BR': str =>
+ /^[A-Z]{3}[ -]?[0-9][A-Z][0-9]{2}|[A-Z]{3}[ -]?[0-9]{4}$/.test(str),
+ 'pt-PT': str =>
+ /^(([A-Z]{2}[ -·]?[0-9]{2}[ -·]?[0-9]{2})|([0-9]{2}[ -·]?[A-Z]{2}[ -·]?[0-9]{2})|([0-9]{2}[ -·]?[0-9]{2}[ -·]?[A-Z]{2})|([A-Z]{2}[ -·]?[0-9]{2}[ -·]?[A-Z]{2}))$/.test(str),
+ 'sq-AL': str =>
+ /^[A-Z]{2}[- ]?((\d{3}[- ]?(([A-Z]{2})|T))|(R[- ]?\d{3}))$/.test(str),
+ 'sv-SE': str =>
+ /^[A-HJ-PR-UW-Z]{3} ?[\d]{2}[A-HJ-PR-UW-Z1-9]$|(^[A-ZÅÄÖ ]{2,7}$)/.test(str.trim()),
+ 'en-PK': str => /(^[A-Z]{2}((\s|-){0,1})[0-9]{3,4}((\s|-)[0-9]{2}){0,1}$)|(^[A-Z]{3}((\s|-){0,1})[0-9]{3,4}((\s|-)[0-9]{2}){0,1}$)|(^[A-Z]{4}((\s|-){0,1})[0-9]{3,4}((\s|-)[0-9]{2}){0,1}$)|(^[A-Z]((\s|-){0,1})[0-9]{4}((\s|-)[0-9]{2}){0,1}$)/.test(str.trim()),
+};
+
+export default function isLicensePlate(str, locale) {
+ assertString(str);
+ if (locale in validators) {
+ return validators[locale](str);
+ } else if (locale === 'any') {
+ for (const key in validators) {
+ /* eslint guard-for-in: 0 */
+ const validator = validators[key];
+ if (validator(str)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ throw new Error(`Invalid locale '${locale}'`);
+}
diff --git a/src/lib/isLocale.js b/src/lib/isLocale.js
new file mode 100644
index 000000000..ec84c8fce
--- /dev/null
+++ b/src/lib/isLocale.js
@@ -0,0 +1,111 @@
+import assertString from './util/assertString';
+
+/*
+ = 3ALPHA ; selected ISO 639 codes
+ *2("-" 3ALPHA) ; permanently reserved
+ */
+const extlang = '([A-Za-z]{3}(-[A-Za-z]{3}){0,2})';
+
+/*
+ = 2*3ALPHA ; shortest ISO 639 code
+ ["-" extlang] ; sometimes followed by
+ ; extended language subtags
+ / 4ALPHA ; or reserved for future use
+ / 5*8ALPHA ; or registered language subtag
+ */
+const language = `(([a-zA-Z]{2,3}(-${extlang})?)|([a-zA-Z]{5,8}))`;
+
+/*
+ = 4ALPHA ; ISO 15924 code
+ */
+const script = '([A-Za-z]{4})';
+
+/*
+ = 2ALPHA ; ISO 3166-1 code
+ / 3DIGIT ; UN M.49 code
+ */
+const region = '([A-Za-z]{2}|\\d{3})';
+
+/*
+ = 5*8alphanum ; registered variants
+ / (DIGIT 3alphanum)
+ */
+const variant = '([A-Za-z0-9]{5,8}|(\\d[A-Z-a-z0-9]{3}))';
+
+/*
+ = DIGIT ; 0 - 9
+ / %x41-57 ; A - W
+ / %x59-5A ; Y - Z
+ / %x61-77 ; a - w
+ / %x79-7A ; y - z
+ */
+const singleton = '(\\d|[A-W]|[Y-Z]|[a-w]|[y-z])';
+
+/*
+ = singleton 1*("-" (2*8alphanum))
+ ; Single alphanumerics
+ ; "x" reserved for private use
+ */
+const extension = `(${singleton}(-[A-Za-z0-9]{2,8})+)`;
+
+/*
+ = "x" 1*("-" (1*8alphanum))
+ */
+const privateuse = '(x(-[A-Za-z0-9]{1,8})+)';
+
+// irregular tags do not match the 'langtag' production and would not
+// otherwise be considered 'well-formed'. These tags are all valid, but
+// most are deprecated in favor of more modern subtags or subtag combination
+
+const irregular = '((en-GB-oed)|(i-ami)|(i-bnn)|(i-default)|(i-enochian)|' +
+ '(i-hak)|(i-klingon)|(i-lux)|(i-mingo)|(i-navajo)|(i-pwn)|(i-tao)|' +
+ '(i-tay)|(i-tsu)|(sgn-BE-FR)|(sgn-BE-NL)|(sgn-CH-DE))';
+
+// regular tags match the 'langtag' production, but their subtags are not
+// extended language or variant subtags: their meaning is defined by
+// their registration and all of these are deprecated in favor of a more
+// modern subtag or sequence of subtags
+
+const regular = '((art-lojban)|(cel-gaulish)|(no-bok)|(no-nyn)|(zh-guoyu)|' +
+ '(zh-hakka)|(zh-min)|(zh-min-nan)|(zh-xiang))';
+
+/*
+ = irregular ; non-redundant tags registered
+ / regular ; during the RFC 3066 era
+
+ */
+const grandfathered = `(${irregular}|${regular})`;
+
+/*
+ RFC 5646 defines delimitation of subtags via a hyphen:
+
+ "Subtag" refers to a specific section of a tag, delimited by a
+ hyphen, such as the subtags 'zh', 'Hant', and 'CN' in the tag "zh-
+ Hant-CN". Examples of subtags in this document are enclosed in
+ single quotes ('Hant')
+
+ However, we need to add "_" to maintain the existing behaviour.
+ */
+const delimiter = '(-|_)';
+
+/*
+ = language
+ ["-" script]
+ ["-" region]
+ *("-" variant)
+ *("-" extension)
+ ["-" privateuse]
+ */
+const langtag = `${language}(${delimiter}${script})?(${delimiter}${region})?(${delimiter}${variant})*(${delimiter}${extension})*(${delimiter}${privateuse})?`;
+
+/*
+ Regex implementation based on BCP RFC 5646
+ Tags for Identifying Languages
+ https://www.rfc-editor.org/rfc/rfc5646.html
+ */
+const languageTagRegex = new RegExp(`(^${privateuse}$)|(^${grandfathered}$)|(^${langtag}$)`);
+
+export default function isLocale(str) {
+ assertString(str);
+ return languageTagRegex.test(str);
+}
diff --git a/src/lib/isLuhnNumber.js b/src/lib/isLuhnNumber.js
new file mode 100644
index 000000000..95a066115
--- /dev/null
+++ b/src/lib/isLuhnNumber.js
@@ -0,0 +1,26 @@
+import assertString from './util/assertString';
+
+export default function isLuhnNumber(str) {
+ assertString(str);
+ const sanitized = str.replace(/[- ]+/g, '');
+ let sum = 0;
+ let digit;
+ let tmpNum;
+ let shouldDouble;
+ for (let i = sanitized.length - 1; i >= 0; i--) {
+ digit = sanitized.substring(i, (i + 1));
+ tmpNum = parseInt(digit, 10);
+ if (shouldDouble) {
+ tmpNum *= 2;
+ if (tmpNum >= 10) {
+ sum += ((tmpNum % 10) + 1);
+ } else {
+ sum += tmpNum;
+ }
+ } else {
+ sum += tmpNum;
+ }
+ shouldDouble = !shouldDouble;
+ }
+ return !!((sum % 10) === 0 ? sanitized : false);
+}
diff --git a/src/lib/isMACAddress.js b/src/lib/isMACAddress.js
index 78647bd0e..d87cd4aa7 100644
--- a/src/lib/isMACAddress.js
+++ b/src/lib/isMACAddress.js
@@ -1,8 +1,34 @@
import assertString from './util/assertString';
-const macAddress = /^([0-9a-fA-F][0-9a-fA-F]:){5}([0-9a-fA-F][0-9a-fA-F])$/;
+const macAddress48 = /^(?:[0-9a-fA-F]{2}([-:\s]))([0-9a-fA-F]{2}\1){4}([0-9a-fA-F]{2})$/;
+const macAddress48NoSeparators = /^([0-9a-fA-F]){12}$/;
+const macAddress48WithDots = /^([0-9a-fA-F]{4}\.){2}([0-9a-fA-F]{4})$/;
+const macAddress64 = /^(?:[0-9a-fA-F]{2}([-:\s]))([0-9a-fA-F]{2}\1){6}([0-9a-fA-F]{2})$/;
+const macAddress64NoSeparators = /^([0-9a-fA-F]){16}$/;
+const macAddress64WithDots = /^([0-9a-fA-F]{4}\.){3}([0-9a-fA-F]{4})$/;
-export default function isMACAddress(str) {
+export default function isMACAddress(str, options) {
assertString(str);
- return macAddress.test(str);
+ if (options?.eui) {
+ options.eui = String(options.eui);
+ }
+ /**
+ * @deprecated `no_colons` TODO: remove it in the next major
+ */
+ if (options?.no_colons || options?.no_separators) {
+ if (options.eui === '48') {
+ return macAddress48NoSeparators.test(str);
+ }
+ if (options.eui === '64') {
+ return macAddress64NoSeparators.test(str);
+ }
+ return macAddress48NoSeparators.test(str) || macAddress64NoSeparators.test(str);
+ }
+ if (options?.eui === '48') {
+ return macAddress48.test(str) || macAddress48WithDots.test(str);
+ }
+ if (options?.eui === '64') {
+ return macAddress64.test(str) || macAddress64WithDots.test(str);
+ }
+ return isMACAddress(str, { eui: '48' }) || isMACAddress(str, { eui: '64' });
}
diff --git a/src/lib/isMagnetURI.js b/src/lib/isMagnetURI.js
new file mode 100644
index 000000000..e00ee3c32
--- /dev/null
+++ b/src/lib/isMagnetURI.js
@@ -0,0 +1,13 @@
+import assertString from './util/assertString';
+
+const magnetURIComponent = /(?:^magnet:\?|[^?&]&)xt(?:\.1)?=urn:(?:(?:aich|bitprint|btih|ed2k|ed2khash|kzhash|md5|sha1|tree:tiger):[a-z0-9]{32}(?:[a-z0-9]{8})?|btmh:1220[a-z0-9]{64})(?:$|&)/i;
+
+export default function isMagnetURI(url) {
+ assertString(url);
+
+ if (url.indexOf('magnet:?') !== 0) {
+ return false;
+ }
+
+ return magnetURIComponent.test(url);
+}
diff --git a/src/lib/isMailtoURI.js b/src/lib/isMailtoURI.js
new file mode 100644
index 000000000..67748a553
--- /dev/null
+++ b/src/lib/isMailtoURI.js
@@ -0,0 +1,67 @@
+import trim from './trim';
+import isEmail from './isEmail';
+import assertString from './util/assertString';
+
+function parseMailtoQueryString(queryString) {
+ const allowedParams = new Set(['subject', 'body', 'cc', 'bcc']),
+ query = { cc: '', bcc: '' };
+ let isParseFailed = false;
+
+ const queryParams = queryString.split('&');
+
+ if (queryParams.length > 4) {
+ return false;
+ }
+
+ for (const q of queryParams) {
+ const [key, value] = q.split('=');
+
+ // checked for invalid and duplicated query params
+ if (key && !allowedParams.has(key)) {
+ isParseFailed = true;
+ break;
+ }
+
+ if (value && (key === 'cc' || key === 'bcc')) {
+ query[key] = value;
+ }
+
+ if (key) {
+ allowedParams.delete(key);
+ }
+ }
+
+ return isParseFailed ? false : query;
+}
+
+export default function isMailtoURI(url, options) {
+ assertString(url);
+
+ if (url.indexOf('mailto:') !== 0) {
+ return false;
+ }
+
+ const [to, queryString = ''] = url.replace('mailto:', '').split('?');
+
+ if (!to && !queryString) {
+ return true;
+ }
+
+ const query = parseMailtoQueryString(queryString);
+
+ if (!query) {
+ return false;
+ }
+
+ return `${to},${query.cc},${query.bcc}`
+ .split(',')
+ .every((email) => {
+ email = trim(email, ' ');
+
+ if (email) {
+ return isEmail(email, options);
+ }
+
+ return true;
+ });
+}
diff --git a/src/lib/isMimeType.js b/src/lib/isMimeType.js
new file mode 100644
index 000000000..820fa4dc2
--- /dev/null
+++ b/src/lib/isMimeType.js
@@ -0,0 +1,40 @@
+import assertString from './util/assertString';
+
+/*
+ Checks if the provided string matches to a correct Media type format (MIME type)
+
+ This function only checks is the string format follows the
+ established rules by the according RFC specifications.
+ This function supports 'charset' in textual media types
+ (https://tools.ietf.org/html/rfc6657).
+
+ This function does not check against all the media types listed
+ by the IANA (https://www.iana.org/assignments/media-types/media-types.xhtml)
+ because of lightness purposes : it would require to include
+ all these MIME types in this library, which would weigh it
+ significantly. This kind of effort maybe is not worth for the use that
+ this function has in this entire library.
+
+ More information in the RFC specifications :
+ - https://tools.ietf.org/html/rfc2045
+ - https://tools.ietf.org/html/rfc2046
+ - https://tools.ietf.org/html/rfc7231#section-3.1.1.1
+ - https://tools.ietf.org/html/rfc7231#section-3.1.1.5
+*/
+
+// Match simple MIME types
+// NB :
+// Subtype length must not exceed 100 characters.
+// This rule does not comply to the RFC specs (what is the max length ?).
+const mimeTypeSimple = /^(application|audio|font|image|message|model|multipart|text|video)\/[a-zA-Z0-9\.\-\+_]{1,100}$/i; // eslint-disable-line max-len
+
+// Handle "charset" in "text/*"
+const mimeTypeText = /^text\/[a-zA-Z0-9\.\-\+]{1,100};\s?charset=("[a-zA-Z0-9\.\-\+\s]{0,70}"|[a-zA-Z0-9\.\-\+]{0,70})(\s?\([a-zA-Z0-9\.\-\+\s]{1,20}\))?$/i; // eslint-disable-line max-len
+
+// Handle "boundary" in "multipart/*"
+const mimeTypeMultipart = /^multipart\/[a-zA-Z0-9\.\-\+]{1,100}(;\s?(boundary|charset)=("[a-zA-Z0-9\.\-\+\s]{0,70}"|[a-zA-Z0-9\.\-\+]{0,70})(\s?\([a-zA-Z0-9\.\-\+\s]{1,20}\))?){0,2}$/i; // eslint-disable-line max-len
+
+export default function isMimeType(str) {
+ assertString(str);
+ return mimeTypeSimple.test(str) || mimeTypeText.test(str) || mimeTypeMultipart.test(str);
+}
diff --git a/src/lib/isMobilePhone.js b/src/lib/isMobilePhone.js
index 4823945b9..b00391ea6 100644
--- a/src/lib/isMobilePhone.js
+++ b/src/lib/isMobilePhone.js
@@ -2,75 +2,202 @@ import assertString from './util/assertString';
/* eslint-disable max-len */
const phones = {
+ 'am-AM': /^(\+?374|0)(33|4[134]|55|77|88|9[13-689])\d{6}$/,
'ar-AE': /^((\+?971)|0)?5[024568]\d{7}$/,
+ 'ar-BH': /^(\+?973)?(3|6)\d{7}$/,
'ar-DZ': /^(\+?213|0)(5|6|7)\d{8}$/,
- 'ar-EG': /^((\+?20)|0)?1[012]\d{8}$/,
+ 'ar-LB': /^(\+?961)?((3|81)\d{6}|7\d{7})$/,
+ 'ar-EG': /^((\+?20)|0)?1[0125]\d{8}$/,
+ 'ar-IQ': /^(\+?964|0)?7[0-9]\d{8}$/,
'ar-JO': /^(\+?962|0)?7[789]\d{7}$/,
+ 'ar-KW': /^(\+?965)([569]\d{7}|41\d{6})$/,
+ 'ar-LY': /^((\+?218)|0)?(9[1-6]\d{7}|[1-8]\d{7,9})$/,
+ 'ar-MA': /^(?:(?:\+|00)212|0)[5-7]\d{8}$/,
+ 'ar-OM': /^((\+|00)968)?([79][1-9])\d{6}$/,
+ 'ar-PS': /^(\+?970|0)5[6|9](\d{7})$/,
'ar-SA': /^(!?(\+?966)|0)?5\d{8}$/,
+ 'ar-SD': /^((\+?249)|0)?(9[012369]|1[012])\d{7}$/,
'ar-SY': /^(!?(\+?963)|0)?9\d{8}$/,
+ 'ar-TN': /^(\+?216)?[2459]\d{7}$/,
+ 'az-AZ': /^(\+994|0)(10|5[015]|7[07]|99)\d{7}$/,
+ 'ar-QA': /^(\+?974|0)?([3567]\d{7})$/,
+ 'bs-BA': /^((((\+|00)3876)|06))((([0-3]|[5-6])\d{6})|(4\d{7}))$/,
+ 'be-BY': /^(\+?375)?(24|25|29|33|44)\d{7}$/,
+ 'bg-BG': /^(\+?359|0)?8[789]\d{7}$/,
+ 'bn-BD': /^(\+?880|0)1[13456789][0-9]{8}$/,
+ 'ca-AD': /^(\+376)?[346]\d{5}$/,
'cs-CZ': /^(\+?420)? ?[1-9][0-9]{2} ?[0-9]{3} ?[0-9]{3}$/,
'da-DK': /^(\+?45)?\s?\d{2}\s?\d{2}\s?\d{2}\s?\d{2}$/,
- 'de-DE': /^(\+?49[ \.\-])?([\(]{1}[0-9]{1,6}[\)])?([0-9 \.\-\/]{3,20})((x|ext|extension)[ ]?[0-9]{1,4})?$/,
- 'el-GR': /^(\+?30)?(69\d{8})$/,
+ 'de-DE': /^((\+49|0)1)(5[0-25-9]\d|6([23]|0\d?)|7([0-57-9]|6\d))\d{7,9}$/,
+ 'de-AT': /^(\+43|0)\d{1,4}\d{3,12}$/,
+ 'de-CH': /^(\+41|0)([1-9])\d{1,9}$/,
+ 'de-LU': /^(\+352)?((6\d1)\d{6})$/,
+ 'dv-MV': /^(\+?960)?(7[2-9]|9[1-9])\d{5}$/,
+ 'el-GR': /^(\+?30|0)?6(8[5-9]|9(?![26])[0-9])\d{7}$/,
+ 'el-CY': /^(\+?357?)?(9(9|7|6|5|4)\d{6})$/,
+ 'en-AI': /^(\+?1|0)264(?:2(35|92)|4(?:6[1-2]|76|97)|5(?:3[6-9]|8[1-4])|7(?:2(4|9)|72))\d{4}$/,
'en-AU': /^(\+?61|0)4\d{8}$/,
- 'en-GB': /^(\+?44|0)7\d{9}$/,
- 'en-HK': /^(\+?852\-?)?[456789]\d{3}\-?\d{4}$/,
- 'en-IN': /^(\+?91|0)?[789]\d{9}$/,
- 'en-KE': /^(\+?254|0)?[7]\d{8}$/,
+ 'en-AG': /^(?:\+1|1)268(?:464|7(?:1[3-9]|[28]\d|3[0246]|64|7[0-689]))\d{4}$/,
+ 'en-BM': /^(\+?1)?441(((3|7)\d{6}$)|(5[0-3][0-9]\d{4}$)|(59\d{5}$))/,
+ 'en-BS': /^(\+?1[-\s]?|0)?\(?242\)?[-\s]?\d{3}[-\s]?\d{4}$/,
+ 'en-GB': /^(\+?44|0)7[1-9]\d{8}$/,
+ 'en-GG': /^(\+?44|0)1481\d{6}$/,
+ 'en-GH': /^(\+233|0)(20|50|24|54|27|57|26|56|23|53|28|55|59)\d{7}$/,
+ 'en-GY': /^(\+592|0)6\d{6}$/,
+ 'en-HK': /^(\+?852[-\s]?)?[456789]\d{3}[-\s]?\d{4}$/,
+ 'en-MO': /^(\+?853[-\s]?)?[6]\d{3}[-\s]?\d{4}$/,
+ 'en-IE': /^(\+?353|0)8[356789]\d{7}$/,
+ 'en-IN': /^(\+?91|0)?[6789]\d{9}$/,
+ 'en-JM': /^(\+?876)?\d{7}$/,
+ 'en-KE': /^(\+?254|0)(7|1)\d{8}$/,
+ 'fr-CF': /^(\+?236| ?)(70|75|77|72|21|22)\d{6}$/,
+ 'en-SS': /^(\+?211|0)(9[1257])\d{7}$/,
+ 'en-KI': /^((\+686|686)?)?( )?((6|7)(2|3|8)[0-9]{6})$/,
+ 'en-KN': /^(?:\+1|1)869(?:46\d|48[89]|55[6-8]|66\d|76[02-7])\d{4}$/,
+ 'en-LS': /^(\+?266)(22|28|57|58|59|27|52)\d{6}$/,
+ 'en-MT': /^(\+?356|0)?(99|79|77|21|27|22|25)[0-9]{6}$/,
+ 'en-MU': /^(\+?230|0)?\d{8}$/,
+ 'en-MW': /^(\+?265|0)(((77|88|31|99|98|21)\d{7})|(((111)|1)\d{6})|(32000\d{4}))$/,
+ 'en-NA': /^(\+?264|0)(6|8)\d{7}$/,
'en-NG': /^(\+?234|0)?[789]\d{9}$/,
- 'en-NZ': /^(\+?64|0)2\d{7,9}$/,
- 'en-PK': /^((\+92)|(0092))-{0,1}\d{3}-{0,1}\d{7}$|^\d{11}$|^\d{4}-\d{7}$/,
+ 'en-NZ': /^(\+?64|0)[28]\d{7,9}$/,
+ 'en-PG': /^(\+?675|0)?(7\d|8[18])\d{6}$/,
+ 'en-PK': /^((00|\+)?92|0)3[0-6]\d{8}$/,
+ 'en-PH': /^(09|\+639)\d{9}$/,
'en-RW': /^(\+?250|0)?[7]\d{8}$/,
- 'en-SG': /^(\+65)?[89]\d{7}$/,
+ 'en-SG': /^(\+65)?[3689]\d{7}$/,
+ 'en-SL': /^(\+?232|0)\d{8}$/,
'en-TZ': /^(\+?255|0)?[67]\d{8}$/,
'en-UG': /^(\+?256|0)?[7]\d{8}$/,
- 'en-US': /^(\+?1)?[2-9]\d{2}[2-9](?!11)\d{6}$/,
+ 'en-US': /^((\+1|1)?( |-)?)?(\([2-9][0-9]{2}\)|[2-9][0-9]{2})( |-)?([2-9][0-9]{2}( |-)?[0-9]{4})$/,
'en-ZA': /^(\+?27|0)\d{9}$/,
- 'en-ZM': /^(\+?26)?09[567]\d{7}$/,
- 'es-ES': /^(\+?34)?(6\d{1}|7[1234])\d{7}$/,
+ 'en-ZM': /^(\+?26)?0[79][567]\d{7}$/,
+ 'en-ZW': /^(\+263)[0-9]{9}$/,
+ 'en-BW': /^(\+?267)?(7[1-8]{1})\d{6}$/,
+ 'es-AR': /^\+?549(11|[2368]\d)\d{8}$/,
+ 'es-BO': /^(\+?591)?(6|7)\d{7}$/,
+ 'es-CO': /^(\+?57)?3(0(0|1|2|4|5)|1\d|2[0-4]|5(0|1))\d{7}$/,
+ 'es-CL': /^(\+?56|0)[2-9]\d{1}\d{7}$/,
+ 'es-CR': /^(\+506)?[2-8]\d{7}$/,
+ 'es-CU': /^(\+53|0053)?5\d{7}$/,
+ 'es-DO': /^(\+?1)?8[024]9\d{7}$/,
+ 'es-HN': /^(\+?504)?[9|8|3|2]\d{7}$/,
+ 'es-EC': /^(\+?593|0)([2-7]|9[2-9])\d{7}$/,
+ 'es-ES': /^(\+?34)?[6|7]\d{8}$/,
+ 'es-GT': /^(\+?502)?[2|6|7]\d{7}$/,
+ 'es-PE': /^(\+?51)?9\d{8}$/,
+ 'es-MX': /^(\+?52)?(1|01)?\d{10,11}$/,
+ 'es-NI': /^(\+?505)\d{7,8}$/,
+ 'es-PA': /^(\+?507)\d{7,8}$/,
+ 'es-PY': /^(\+?595|0)9[9876]\d{7}$/,
+ 'es-SV': /^(\+?503)?[67]\d{7}$/,
+ 'es-UY': /^(\+598|0)9[1-9][\d]{6}$/,
+ 'es-VE': /^(\+?58)?(2|4)\d{9}$/,
'et-EE': /^(\+?372)?\s?(5|8[1-4])\s?([0-9]\s?){6,7}$/,
'fa-IR': /^(\+?98[\-\s]?|0)9[0-39]\d[\-\s]?\d{3}[\-\s]?\d{4}$/,
- 'fi-FI': /^(\+?358|0)\s?(4(0|1|2|4|5|6)?|50)\s?(\d\s?){4,8}\d$/,
+ 'fi-FI': /^(\+?358|0)\s?(4[0-6]|50)\s?(\d\s?){4,8}$/,
+ 'fj-FJ': /^(\+?679)?\s?\d{3}\s?\d{4}$/,
'fo-FO': /^(\+?298)?\s?\d{2}\s?\d{2}\s?\d{2}$/,
+ 'fr-BF': /^(\+226|0)[67]\d{7}$/,
+ 'fr-BJ': /^(\+229)\d{8}$/,
+ 'fr-CD': /^(\+?243|0)?(8|9)\d{8}$/,
+ 'fr-CM': /^(\+?237)6[0-9]{8}$/,
'fr-FR': /^(\+?33|0)[67]\d{8}$/,
- 'he-IL': /^(\+972|0)([23489]|5[0248]|77)[1-9]\d{6}/,
- 'hu-HU': /^(\+?36)(20|30|70)\d{7}$/,
- 'id-ID': /^(\+?62|0[1-9])[\s|\d]+$/,
+ 'fr-GF': /^(\+?594|0|00594)[67]\d{8}$/,
+ 'fr-GP': /^(\+?590|0|00590)[67]\d{8}$/,
+ 'fr-MQ': /^(\+?596|0|00596)[67]\d{8}$/,
+ 'fr-PF': /^(\+?689)?8[789]\d{6}$/,
+ 'fr-RE': /^(\+?262|0|00262)[67]\d{8}$/,
+ 'fr-WF': /^(\+681)?\d{6}$/,
+ 'he-IL': /^(\+972|0)([23489]|5[012345689]|77)[1-9]\d{6}$/,
+ 'hu-HU': /^(\+?36|06)(20|30|31|50|70)\d{7}$/,
+ 'id-ID': /^(\+?62|0)8(1[123456789]|2[1238]|3[1238]|5[12356789]|7[78]|9[56789]|8[123456789])([\s?|\d]{5,11})$/,
+ 'ir-IR': /^(\+98|0)?9\d{9}$/,
'it-IT': /^(\+?39)?\s?3\d{2} ?\d{6,7}$/,
- 'ja-JP': /^(\+?81|0)[789]0[ \-]?[1-9]\d{2}[ \-]?\d{5}$/,
+ 'it-SM': /^((\+378)|(0549)|(\+390549)|(\+3780549))?6\d{5,9}$/,
+ 'ja-JP': /^(\+81[ \-]?(\(0\))?|0)[6789]0[ \-]?\d{4}[ \-]?\d{4}$/,
+ 'ka-GE': /^(\+?995)?(79\d{7}|5\d{8})$/,
+ 'kk-KZ': /^(\+?7|8)?7\d{9}$/,
'kl-GL': /^(\+?299)?\s?\d{2}\s?\d{2}\s?\d{2}$/,
'ko-KR': /^((\+?82)[ \-]?)?0?1([0|1|6|7|8|9]{1})[ \-]?\d{3,4}[ \-]?\d{4}$/,
+ 'ky-KG': /^(\+996\s?)?(22[0-9]|50[0-9]|55[0-9]|70[0-9]|75[0-9]|77[0-9]|880|990|995|996|997|998)\s?\d{3}\s?\d{3}$/,
'lt-LT': /^(\+370|8)\d{8}$/,
- 'ms-MY': /^(\+?6?01){1}(([145]{1}(\-|\s)?\d{7,8})|([236789]{1}(\s|\-)?\d{7}))$/,
+ 'lv-LV': /^(\+?371)2\d{7}$/,
+ 'mg-MG': /^((\+?261|0)(2|3)\d)?\d{7}$/,
+ 'mn-MN': /^(\+|00|011)?976(77|81|88|91|94|95|96|99)\d{6}$/,
+ 'my-MM': /^(\+?959|09|9)(2[5-7]|3[1-2]|4[0-5]|6[6-9]|7[5-9]|9[6-9])[0-9]{7}$/,
+ 'ms-MY': /^(\+?60|0)1(([0145](-|\s)?\d{7,8})|([236-9](-|\s)?\d{7}))$/,
+ 'mz-MZ': /^(\+?258)?8[234567]\d{7}$/,
'nb-NO': /^(\+?47)?[49]\d{7}$/,
- 'nl-BE': /^(\+?32|0)4?\d{8}$/,
+ 'ne-NP': /^(\+?977)?9[78]\d{8}$/,
+ 'nl-BE': /^(\+?32|0)4\d{8}$/,
+ 'nl-NL': /^(((\+|00)?31\(0\))|((\+|00)?31)|0)6{1}\d{8}$/,
+ 'nl-AW': /^(\+)?297(56|59|64|73|74|99)\d{5}$/,
'nn-NO': /^(\+?47)?[49]\d{7}$/,
- 'pl-PL': /^(\+?48)? ?[5-8]\d ?\d{3} ?\d{2} ?\d{2}$/,
- 'pt-BR': /^(\+?55|0)\-?[1-9]{2}\-?[2-9]{1}\d{3,4}\-?\d{4}$/,
+ 'pl-PL': /^(\+?48)? ?([5-8]\d|45) ?\d{3} ?\d{2} ?\d{2}$/,
+ 'pt-BR': /^((\+?55\ ?[1-9]{2}\ ?)|(\+?55\ ?\([1-9]{2}\)\ ?)|(0[1-9]{2}\ ?)|(\([1-9]{2}\)\ ?)|([1-9]{2}\ ?))((\d{4}\-?\d{4})|(9[1-9]{1}\d{3}\-?\d{4}))$/,
'pt-PT': /^(\+?351)?9[1236]\d{7}$/,
- 'ro-RO': /^(\+?4?0)\s?7\d{2}(\/|\s|\.|\-)?\d{3}(\s|\.|\-)?\d{3}$/,
+ 'pt-AO': /^(\+?244)?9\d{8}$/,
+ 'ro-MD': /^(\+?373|0)((6(0|1|2|6|7|8|9))|(7(6|7|8|9)))\d{6}$/,
+ 'ro-RO': /^(\+?40|0)\s?7\d{2}(\/|\s|\.|-)?\d{3}(\s|\.|-)?\d{3}$/,
'ru-RU': /^(\+?7|8)?9\d{9}$/,
+ 'si-LK': /^(?:0|94|\+94)?(7(0|1|2|4|5|6|7|8)( |-)?)\d{7}$/,
+ 'sl-SI': /^(\+386\s?|0)(\d{1}\s?\d{3}\s?\d{2}\s?\d{2}|\d{2}\s?\d{3}\s?\d{3})$/,
'sk-SK': /^(\+?421)? ?[1-9][0-9]{2} ?[0-9]{3} ?[0-9]{3}$/,
+ 'so-SO': /^(\+?252|0)((6[0-9])\d{7}|(7[1-9])\d{7})$/,
+ 'sq-AL': /^(\+355|0)6[2-9]\d{7}$/,
'sr-RS': /^(\+3816|06)[- \d]{5,9}$/,
+ 'sv-SE': /^(\+?46|0)[\s\-]?7[\s\-]?[02369]([\s\-]?\d){7}$/,
+ 'tg-TJ': /^(\+?992)?[5][5]\d{7}$/,
+ 'th-TH': /^(\+66|66|0)\d{9}$/,
'tr-TR': /^(\+?90|0)?5\d{9}$/,
- 'uk-UA': /^(\+?38|8)?0\d{9}$/,
- 'vi-VN': /^(\+?84|0)?((1(2([0-9])|6([2-9])|88|99))|(9((?!5)[0-9])))([0-9]{7})$/,
- 'zh-CN': /^(\+?0?86\-?)?1[345789]\d{9}$/,
+ 'tk-TM': /^(\+993|993|8)\d{8}$/,
+ 'uk-UA': /^(\+?38)?0(50|6[36-8]|7[357]|9[1-9])\d{7}$/,
+ 'uz-UZ': /^(\+?998)?(6[125-79]|7[1-69]|88|9\d)\d{7}$/,
+ 'vi-VN': /^((\+?84)|0)((3([2-9]))|(5([25689]))|(7([0|6-9]))|(8([1-9]))|(9([0-9])))([0-9]{7})$/,
+ 'zh-CN': /^((\+|00)86)?(1[3-9]|9[28])\d{9}$/,
'zh-TW': /^(\+?886\-?|0)?9\d{8}$/,
+ 'dz-BT': /^(\+?975|0)?(17|16|77|02)\d{6}$/,
+ 'ar-YE': /^(((\+|00)9677|0?7)[0137]\d{7}|((\+|00)967|0)[1-7]\d{6})$/,
+ 'ar-EH': /^(\+?212|0)[\s\-]?(5288|5289)[\s\-]?\d{5}$/,
+ 'fa-AF': /^(\+93|0)?(2{1}[0-8]{1}|[3-5]{1}[0-4]{1})(\d{7})$/,
+ 'mk-MK': /^(\+?389|0)?((?:2[2-9]\d{6}|(?:3[1-4]|4[2-8])\d{6}|500\d{5}|5[2-9]\d{6}|7[0-9][2-9]\d{5}|8[1-9]\d{6}|800\d{5}|8009\d{4}))$/,
};
/* eslint-enable max-len */
// aliases
phones['en-CA'] = phones['en-US'];
+phones['fr-CA'] = phones['en-CA'];
phones['fr-BE'] = phones['nl-BE'];
phones['zh-HK'] = phones['en-HK'];
+phones['zh-MO'] = phones['en-MO'];
+phones['ga-IE'] = phones['en-IE'];
+phones['fr-CH'] = phones['de-CH'];
+phones['it-CH'] = phones['fr-CH'];
-export default function isMobilePhone(str, locale) {
+export default function isMobilePhone(str, locale, options) {
assertString(str);
- if (locale in phones) {
+ if (options && options.strictMode && !str.startsWith('+')) {
+ return false;
+ }
+ if (Array.isArray(locale)) {
+ return locale.some((key) => {
+ // https://github.com/gotwarlost/istanbul/blob/master/ignoring-code-for-coverage.md#ignoring-code-for-coverage-purposes
+ // istanbul ignore else
+ if (phones.hasOwnProperty(key)) {
+ const phone = phones[key];
+ if (phone.test(str)) {
+ return true;
+ }
+ }
+ return false;
+ });
+ } else if (locale in phones) {
return phones[locale].test(str);
- } else if (locale === 'any') {
+ // alias falsey locale as 'any'
+ } else if (!locale || locale === 'any') {
for (const key in phones) {
+ // istanbul ignore else
if (phones.hasOwnProperty(key)) {
const phone = phones[key];
if (phone.test(str)) {
@@ -82,3 +209,5 @@ export default function isMobilePhone(str, locale) {
}
throw new Error(`Invalid locale '${locale}'`);
}
+
+export const locales = Object.keys(phones);
diff --git a/src/lib/isNumeric.js b/src/lib/isNumeric.js
index 2b703e6e2..4cc7ea5b3 100644
--- a/src/lib/isNumeric.js
+++ b/src/lib/isNumeric.js
@@ -1,8 +1,12 @@
import assertString from './util/assertString';
+import { decimal } from './alpha';
-const numeric = /^[-+]?[0-9]+$/;
+const numericNoSymbols = /^[0-9]+$/;
-export default function isNumeric(str) {
+export default function isNumeric(str, options) {
assertString(str);
- return numeric.test(str);
+ if (options && options.no_symbols) {
+ return numericNoSymbols.test(str);
+ }
+ return (new RegExp(`^[+-]?([0-9]*[${(options || {}).locale ? decimal[options.locale] : '.'}])?[0-9]+$`)).test(str);
}
diff --git a/src/lib/isOctal.js b/src/lib/isOctal.js
new file mode 100644
index 000000000..205b98ab6
--- /dev/null
+++ b/src/lib/isOctal.js
@@ -0,0 +1,8 @@
+import assertString from './util/assertString';
+
+const octal = /^(0o)?[0-7]+$/i;
+
+export default function isOctal(str) {
+ assertString(str);
+ return octal.test(str);
+}
diff --git a/src/lib/isPassportNumber.js b/src/lib/isPassportNumber.js
new file mode 100644
index 000000000..c3b842e59
--- /dev/null
+++ b/src/lib/isPassportNumber.js
@@ -0,0 +1,89 @@
+import assertString from './util/assertString';
+
+/**
+ * Reference:
+ * https://en.wikipedia.org/ -- Wikipedia
+ * https://docs.microsoft.com/en-us/microsoft-365/compliance/eu-passport-number -- EU Passport Number
+ * https://countrycode.org/ -- Country Codes
+ */
+const passportRegexByCountryCode = {
+ AM: /^[A-Z]{2}\d{7}$/, // ARMENIA
+ AR: /^[A-Z]{3}\d{6}$/, // ARGENTINA
+ AT: /^[A-Z]\d{7}$/, // AUSTRIA
+ AU: /^[A-Z]\d{7}$/, // AUSTRALIA
+ AZ: /^[A-Z]{1}\d{8}$/, // AZERBAIJAN
+ BE: /^[A-Z]{2}\d{6}$/, // BELGIUM
+ BG: /^\d{9}$/, // BULGARIA
+ BR: /^[A-Z]{2}\d{6}$/, // BRAZIL
+ BY: /^[A-Z]{2}\d{7}$/, // BELARUS
+ CA: /^[A-Z]{2}\d{6}$|^[A-Z]\d{6}[A-Z]{2}$/, // CANADA
+ CH: /^[A-Z]\d{7}$/, // SWITZERLAND
+ CN: /^G\d{8}$|^E(?![IO])[A-Z0-9]\d{7}$/, // CHINA [G=Ordinary, E=Electronic] followed by 8-digits, or E followed by any UPPERCASE letter (except I and O) followed by 7 digits
+ CY: /^[A-Z](\d{6}|\d{8})$/, // CYPRUS
+ CZ: /^\d{8}$/, // CZECH REPUBLIC
+ DE: /^[CFGHJKLMNPRTVWXYZ0-9]{9}$/, // GERMANY
+ DK: /^\d{9}$/, // DENMARK
+ DZ: /^\d{9}$/, // ALGERIA
+ EE: /^([A-Z]\d{7}|[A-Z]{2}\d{7})$/, // ESTONIA (K followed by 7-digits), e-passports have 2 UPPERCASE followed by 7 digits
+ ES: /^[A-Z0-9]{2}([A-Z0-9]?)\d{6}$/, // SPAIN
+ FI: /^[A-Z]{2}\d{7}$/, // FINLAND
+ FR: /^\d{2}[A-Z]{2}\d{5}$/, // FRANCE
+ GB: /^\d{9}$/, // UNITED KINGDOM
+ GR: /^[A-Z]{2}\d{7}$/, // GREECE
+ HR: /^\d{9}$/, // CROATIA
+ HU: /^[A-Z]{2}(\d{6}|\d{7})$/, // HUNGARY
+ IE: /^[A-Z0-9]{2}\d{7}$/, // IRELAND
+ IN: /^[A-Z]{1}-?\d{7}$/, // INDIA
+ ID: /^[A-C]\d{7}$/, // INDONESIA
+ IR: /^[A-Z]\d{8}$/, // IRAN
+ IS: /^(A)\d{7}$/, // ICELAND
+ IT: /^[A-Z0-9]{2}\d{7}$/, // ITALY
+ JM: /^[Aa]\d{7}$/, // JAMAICA
+ JP: /^[A-Z]{2}\d{7}$/, // JAPAN
+ KR: /^[MS]\d{8}$/, // SOUTH KOREA, REPUBLIC OF KOREA, [S=PS Passports, M=PM Passports]
+ KZ: /^[a-zA-Z]\d{7}$/, // KAZAKHSTAN
+ LI: /^[a-zA-Z]\d{5}$/, // LIECHTENSTEIN
+ LT: /^[A-Z0-9]{8}$/, // LITHUANIA
+ LU: /^[A-Z0-9]{8}$/, // LUXEMBURG
+ LV: /^[A-Z0-9]{2}\d{7}$/, // LATVIA
+ LY: /^[A-Z0-9]{8}$/, // LIBYA
+ MT: /^\d{7}$/, // MALTA
+ MZ: /^([A-Z]{2}\d{7})|(\d{2}[A-Z]{2}\d{5})$/, // MOZAMBIQUE
+ MY: /^[AHK]\d{8}$/, // MALAYSIA
+ MX: /^\d{10,11}$/, // MEXICO
+ NL: /^[A-Z]{2}[A-Z0-9]{6}\d$/, // NETHERLANDS
+ NZ: /^([Ll]([Aa]|[Dd]|[Ff]|[Hh])|[Ee]([Aa]|[Pp])|[Nn])\d{6}$/, // NEW ZEALAND
+ PH: /^([A-Z](\d{6}|\d{7}[A-Z]))|([A-Z]{2}(\d{6}|\d{7}))$/, // PHILIPPINES
+ PK: /^[A-Z]{2}\d{7}$/, // PAKISTAN
+ PL: /^[A-Z]{2}\d{7}$/, // POLAND
+ PT: /^[A-Z]\d{6}$/, // PORTUGAL
+ RO: /^\d{8,9}$/, // ROMANIA
+ RU: /^\d{9}$/, // RUSSIAN FEDERATION
+ SE: /^\d{8}$/, // SWEDEN
+ SL: /^(P)[A-Z]\d{7}$/, // SLOVENIA
+ SK: /^[0-9A-Z]\d{7}$/, // SLOVAKIA
+ TH: /^[A-Z]{1,2}\d{6,7}$/, // THAILAND
+ TR: /^[A-Z]\d{8}$/, // TURKEY
+ UA: /^[A-Z]{2}\d{6}$/, // UKRAINE
+ US: /^\d{9}$|^[A-Z]\d{8}$/, // UNITED STATES
+ ZA: /^[TAMD]\d{8}$/, // SOUTH AFRICA
+};
+
+export const locales = Object.keys(passportRegexByCountryCode);
+
+/**
+ * Check if str is a valid passport number
+ * relative to provided ISO Country Code.
+ *
+ * @param {string} str
+ * @param {string} countryCode
+ * @return {boolean}
+ */
+export default function isPassportNumber(str, countryCode) {
+ assertString(str);
+ /** Remove All Whitespaces, Convert to UPPERCASE */
+ const normalizedStr = str.replace(/\s/g, '').toUpperCase();
+
+ return (countryCode.toUpperCase() in passportRegexByCountryCode) &&
+ passportRegexByCountryCode[countryCode].test(normalizedStr);
+}
diff --git a/src/lib/isPort.js b/src/lib/isPort.js
index 0b316b78c..0a9ddce1d 100644
--- a/src/lib/isPort.js
+++ b/src/lib/isPort.js
@@ -1,5 +1,5 @@
import isInt from './isInt';
export default function isPort(str) {
- return isInt(str, { min: 0, max: 65535 });
+ return isInt(str, { allow_leading_zeroes: false, min: 0, max: 65535 });
}
diff --git a/src/lib/isPostalCode.js b/src/lib/isPostalCode.js
index 5b0b5cbeb..da3fbdcf8 100644
--- a/src/lib/isPostalCode.js
+++ b/src/lib/isPostalCode.js
@@ -7,37 +7,73 @@ const fiveDigit = /^\d{5}$/;
const sixDigit = /^\d{6}$/;
const patterns = {
+ AD: /^AD\d{3}$/,
AT: fourDigit,
AU: fourDigit,
+ AZ: /^AZ\d{4}$/,
+ BA: /^([7-8]\d{4}$)/,
+ BD: /^([1-8][0-9]{3}|9[0-4][0-9]{2})$/,
BE: fourDigit,
+ BG: fourDigit,
+ BR: /^\d{5}-?\d{3}$/,
+ BY: /^2[1-4]\d{4}$/,
CA: /^[ABCEGHJKLMNPRSTVXY]\d[ABCEGHJ-NPRSTV-Z][\s\-]?\d[ABCEGHJ-NPRSTV-Z]\d$/i,
CH: fourDigit,
+ CN: /^(0[1-7]|1[012356]|2[0-7]|3[0-6]|4[0-7]|5[1-7]|6[1-7]|7[1-5]|8[1345]|9[09])\d{4}$/,
+ CO: /^(05|08|11|13|15|17|18|19|20|23|25|27|41|44|47|50|52|54|63|66|68|70|73|76|81|85|86|88|91|94|95|97|99)(\d{4})$/,
CZ: /^\d{3}\s?\d{2}$/,
DE: fiveDigit,
DK: fourDigit,
+ DO: fiveDigit,
DZ: fiveDigit,
- ES: fiveDigit,
+ EE: fiveDigit,
+ ES: /^(5[0-2]{1}|[0-4]{1}\d{1})\d{3}$/,
FI: fiveDigit,
- FR: /^\d{2}\s?\d{3}$/,
+ FR: /^(?:(?:0[1-9]|[1-8]\d|9[0-5])\d{3}|97[1-46]\d{2})$/,
GB: /^(gir\s?0aa|[a-z]{1,2}\d[\da-z]?\s?(\d[a-z]{2})?)$/i,
GR: /^\d{3}\s?\d{2}$/,
- IL: fiveDigit,
- IN: sixDigit,
+ HR: /^([1-5]\d{4}$)/,
+ HT: /^HT\d{4}$/,
+ HU: fourDigit,
+ ID: fiveDigit,
+ IE: /^(?!.*(?:o))[A-Za-z]\d[\dw]\s\w{4}$/i,
+ IL: /^(\d{5}|\d{7})$/,
+ IN: /^((?!10|29|35|54|55|65|66|86|87|88|89)[1-9][0-9]{5})$/,
+ IR: /^(?!(\d)\1{3})[13-9]{4}[1346-9][013-9]{5}$/,
IS: threeDigit,
IT: fiveDigit,
JP: /^\d{3}\-\d{4}$/,
KE: fiveDigit,
+ KR: /^(\d{5}|\d{6})$/,
LI: /^(948[5-9]|949[0-7])$/,
+ LT: /^LT\-\d{5}$/,
+ LU: fourDigit,
+ LV: /^LV\-\d{4}$/,
+ LK: fiveDigit,
+ MG: threeDigit,
MX: fiveDigit,
- NL: /^\d{4}\s?[a-z]{2}$/i,
+ MT: /^[A-Za-z]{3}\s{0,1}\d{4}$/,
+ MY: fiveDigit,
+ NL: /^[1-9]\d{3}\s?(?!sa|sd|ss)[a-z]{2}$/i,
NO: fourDigit,
+ NP: /^(10|21|22|32|33|34|44|45|56|57)\d{3}$|^(977)$/i,
+ NZ: fourDigit,
+ // https://www.pakpost.gov.pk/postcodes.php
+ PK: fiveDigit,
PL: /^\d{2}\-\d{3}$/,
- PT: /^\d{4}(\-\d{3})?$/,
+ PR: /^00[679]\d{2}([ -]\d{4})?$/,
+ PT: /^\d{4}\-\d{3}?$/,
RO: sixDigit,
RU: sixDigit,
SA: fiveDigit,
- SE: /^\d{3}\s?\d{2}$/,
- TW: /^\d{3}(\d{2})?$/,
+ SE: /^[1-9]\d{2}\s?\d{2}$/,
+ SG: sixDigit,
+ SI: fourDigit,
+ SK: /^\d{3}\s?\d{2}$/,
+ TH: fiveDigit,
+ TN: fourDigit,
+ TW: /^\d{3}(\d{2,3})?$/,
+ UA: fiveDigit,
US: /^\d{5}(-\d{4})?$/,
ZA: fourDigit,
ZM: fiveDigit,
@@ -45,12 +81,14 @@ const patterns = {
export const locales = Object.keys(patterns);
-export default function (str, locale) {
+export default function isPostalCode(str, locale) {
assertString(str);
if (locale in patterns) {
return patterns[locale].test(str);
} else if (locale === 'any') {
for (const key in patterns) {
+ // https://github.com/gotwarlost/istanbul/blob/master/ignoring-code-for-coverage.md#ignoring-code-for-coverage-purposes
+ // istanbul ignore else
if (patterns.hasOwnProperty(key)) {
const pattern = patterns[key];
if (pattern.test(str)) {
diff --git a/src/lib/isRFC3339.js b/src/lib/isRFC3339.js
new file mode 100644
index 000000000..48b025e0f
--- /dev/null
+++ b/src/lib/isRFC3339.js
@@ -0,0 +1,27 @@
+import assertString from './util/assertString';
+
+/* Based on https://tools.ietf.org/html/rfc3339#section-5.6 */
+
+const dateFullYear = /[0-9]{4}/;
+const dateMonth = /(0[1-9]|1[0-2])/;
+const dateMDay = /([12]\d|0[1-9]|3[01])/;
+
+const timeHour = /([01][0-9]|2[0-3])/;
+const timeMinute = /[0-5][0-9]/;
+const timeSecond = /([0-5][0-9]|60)/;
+
+const timeSecFrac = /(\.[0-9]+)?/;
+const timeNumOffset = new RegExp(`[-+]${timeHour.source}:${timeMinute.source}`);
+const timeOffset = new RegExp(`([zZ]|${timeNumOffset.source})`);
+
+const partialTime = new RegExp(`${timeHour.source}:${timeMinute.source}:${timeSecond.source}${timeSecFrac.source}`);
+
+const fullDate = new RegExp(`${dateFullYear.source}-${dateMonth.source}-${dateMDay.source}`);
+const fullTime = new RegExp(`${partialTime.source}${timeOffset.source}`);
+
+const rfc3339 = new RegExp(`^${fullDate.source}[ tT]${fullTime.source}$`);
+
+export default function isRFC3339(str) {
+ assertString(str);
+ return rfc3339.test(str);
+}
diff --git a/src/lib/isRgbColor.js b/src/lib/isRgbColor.js
new file mode 100644
index 000000000..6e2866243
--- /dev/null
+++ b/src/lib/isRgbColor.js
@@ -0,0 +1,42 @@
+/* eslint-disable prefer-rest-params */
+import assertString from './util/assertString';
+
+const rgbColor = /^rgb\((([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5]),){2}([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\)$/;
+const rgbaColor = /^rgba\((([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5]),){3}(0?\.\d\d?|1(\.0)?|0(\.0)?)\)$/;
+const rgbColorPercent = /^rgb\((([0-9]%|[1-9][0-9]%|100%),){2}([0-9]%|[1-9][0-9]%|100%)\)$/;
+const rgbaColorPercent = /^rgba\((([0-9]%|[1-9][0-9]%|100%),){3}(0?\.\d\d?|1(\.0)?|0(\.0)?)\)$/;
+const startsWithRgb = /^rgba?/;
+
+export default function isRgbColor(str, options) {
+ assertString(str);
+ // default options to true for percent and false for spaces
+ let allowSpaces = false;
+ let includePercentValues = true;
+ if (typeof options !== 'object') {
+ if (arguments.length >= 2) {
+ includePercentValues = arguments[1];
+ }
+ } else {
+ allowSpaces = options.allowSpaces !== undefined ? options.allowSpaces : allowSpaces;
+ includePercentValues = options.includePercentValues !== undefined ?
+ options.includePercentValues : includePercentValues;
+ }
+
+ if (allowSpaces) {
+ // make sure it starts with continous rgba? without spaces before stripping
+ if (!startsWithRgb.test(str)) {
+ return false;
+ }
+ // strip all whitespace
+ str = str.replace(/\s/g, '');
+ }
+
+ if (!includePercentValues) {
+ return rgbColor.test(str) || rgbaColor.test(str);
+ }
+
+ return rgbColor.test(str) ||
+ rgbaColor.test(str) ||
+ rgbColorPercent.test(str) ||
+ rgbaColorPercent.test(str);
+}
diff --git a/src/lib/isSemVer.js b/src/lib/isSemVer.js
new file mode 100644
index 000000000..172d36496
--- /dev/null
+++ b/src/lib/isSemVer.js
@@ -0,0 +1,20 @@
+import assertString from './util/assertString';
+import multilineRegexp from './util/multilineRegex';
+
+/**
+ * Regular Expression to match
+ * semantic versioning (SemVer)
+ * built from multi-line, multi-parts regexp
+ * Reference: https://semver.org/
+ */
+const semanticVersioningRegex = multilineRegexp([
+ '^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)',
+ '(?:-((?:0|[1-9]\\d*|\\d*[a-z-][0-9a-z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-z-][0-9a-z-]*))*))',
+ '?(?:\\+([0-9a-z-]+(?:\\.[0-9a-z-]+)*))?$',
+], 'i');
+
+export default function isSemVer(str) {
+ assertString(str);
+
+ return semanticVersioningRegex.test(str);
+}
diff --git a/src/lib/isSlug.js b/src/lib/isSlug.js
new file mode 100644
index 000000000..28b872a92
--- /dev/null
+++ b/src/lib/isSlug.js
@@ -0,0 +1,8 @@
+import assertString from './util/assertString';
+
+let charsetRegex = /^[^\s-_](?!.*?[-_]{2,})[a-z0-9-\\][^\s]*[^-_\s]$/;
+
+export default function isSlug(str) {
+ assertString(str);
+ return (charsetRegex.test(str));
+}
diff --git a/src/lib/isStrongPassword.js b/src/lib/isStrongPassword.js
new file mode 100644
index 000000000..8fe9223b7
--- /dev/null
+++ b/src/lib/isStrongPassword.js
@@ -0,0 +1,97 @@
+import merge from './util/merge';
+import assertString from './util/assertString';
+
+const upperCaseRegex = /^[A-Z]$/;
+const lowerCaseRegex = /^[a-z]$/;
+const numberRegex = /^[0-9]$/;
+const symbolRegex = /^[-#!$@£%^&*()_+|~=`{}\[\]:";'<>?,.\/\\ ]$/;
+
+const defaultOptions = {
+ minLength: 8,
+ minLowercase: 1,
+ minUppercase: 1,
+ minNumbers: 1,
+ minSymbols: 1,
+ returnScore: false,
+ pointsPerUnique: 1,
+ pointsPerRepeat: 0.5,
+ pointsForContainingLower: 10,
+ pointsForContainingUpper: 10,
+ pointsForContainingNumber: 10,
+ pointsForContainingSymbol: 10,
+};
+
+/* Counts number of occurrences of each char in a string
+ * could be moved to util/ ?
+*/
+function countChars(str) {
+ let result = {};
+ Array.from(str).forEach((char) => {
+ let curVal = result[char];
+ if (curVal) {
+ result[char] += 1;
+ } else {
+ result[char] = 1;
+ }
+ });
+ return result;
+}
+
+/* Return information about a password */
+function analyzePassword(password) {
+ let charMap = countChars(password);
+ let analysis = {
+ length: password.length,
+ uniqueChars: Object.keys(charMap).length,
+ uppercaseCount: 0,
+ lowercaseCount: 0,
+ numberCount: 0,
+ symbolCount: 0,
+ };
+ Object.keys(charMap).forEach((char) => {
+ /* istanbul ignore else */
+ if (upperCaseRegex.test(char)) {
+ analysis.uppercaseCount += charMap[char];
+ } else if (lowerCaseRegex.test(char)) {
+ analysis.lowercaseCount += charMap[char];
+ } else if (numberRegex.test(char)) {
+ analysis.numberCount += charMap[char];
+ } else if (symbolRegex.test(char)) {
+ analysis.symbolCount += charMap[char];
+ }
+ });
+ return analysis;
+}
+
+function scorePassword(analysis, scoringOptions) {
+ let points = 0;
+ points += analysis.uniqueChars * scoringOptions.pointsPerUnique;
+ points += (analysis.length - analysis.uniqueChars) * scoringOptions.pointsPerRepeat;
+ if (analysis.lowercaseCount > 0) {
+ points += scoringOptions.pointsForContainingLower;
+ }
+ if (analysis.uppercaseCount > 0) {
+ points += scoringOptions.pointsForContainingUpper;
+ }
+ if (analysis.numberCount > 0) {
+ points += scoringOptions.pointsForContainingNumber;
+ }
+ if (analysis.symbolCount > 0) {
+ points += scoringOptions.pointsForContainingSymbol;
+ }
+ return points;
+}
+
+export default function isStrongPassword(str, options = null) {
+ assertString(str);
+ const analysis = analyzePassword(str);
+ options = merge(options || {}, defaultOptions);
+ if (options.returnScore) {
+ return scorePassword(analysis, options);
+ }
+ return analysis.length >= options.minLength
+ && analysis.lowercaseCount >= options.minLowercase
+ && analysis.uppercaseCount >= options.minUppercase
+ && analysis.numberCount >= options.minNumbers
+ && analysis.symbolCount >= options.minSymbols;
+}
diff --git a/src/lib/isTaxID.js b/src/lib/isTaxID.js
new file mode 100644
index 000000000..5e5f8cb5b
--- /dev/null
+++ b/src/lib/isTaxID.js
@@ -0,0 +1,1283 @@
+import assertString from './util/assertString';
+import * as algorithms from './util/algorithms';
+import isDate from './isDate';
+
+/**
+ * TIN Validation
+ * Validates Tax Identification Numbers (TINs) from the US, EU member states and the United Kingdom.
+ *
+ * EU-UK:
+ * National TIN validity is calculated using public algorithms as made available by DG TAXUD.
+ *
+ * See `https://ec.europa.eu/taxation_customs/tin/specs/FS-TIN%20Algorithms-Public.docx` for more information.
+ *
+ * US:
+ * An Employer Identification Number (EIN), also known as a Federal Tax Identification Number,
+ * is used to identify a business entity.
+ *
+ * NOTES:
+ * - Prefix 47 is being reserved for future use
+ * - Prefixes 26, 27, 45, 46 and 47 were previously assigned by the Philadelphia campus.
+ *
+ * See `http://www.irs.gov/Businesses/Small-Businesses-&-Self-Employed/How-EINs-are-Assigned-and-Valid-EIN-Prefixes`
+ * for more information.
+ */
+
+// Locale functions
+
+/*
+ * bg-BG validation function
+ * (Edinen graždanski nomer (EGN/ЕГН), persons only)
+ * Checks if birth date (first six digits) is valid and calculates check (last) digit
+ */
+function bgBgCheck(tin) {
+ // Extract full year, normalize month and check birth date validity
+ let century_year = tin.slice(0, 2);
+ let month = parseInt(tin.slice(2, 4), 10);
+ if (month > 40) {
+ month -= 40;
+ century_year = `20${century_year}`;
+ } else if (month > 20) {
+ month -= 20;
+ century_year = `18${century_year}`;
+ } else {
+ century_year = `19${century_year}`;
+ }
+ if (month < 10) { month = `0${month}`; }
+ const date = `${century_year}/${month}/${tin.slice(4, 6)}`;
+ if (!isDate(date, 'YYYY/MM/DD')) { return false; }
+
+ // split digits into an array for further processing
+ const digits = tin.split('').map(a => parseInt(a, 10));
+
+ // Calculate checksum by multiplying digits with fixed values
+ const multip_lookup = [2, 4, 8, 5, 10, 9, 7, 3, 6];
+ let checksum = 0;
+ for (let i = 0; i < multip_lookup.length; i++) {
+ checksum += digits[i] * multip_lookup[i];
+ }
+ checksum = checksum % 11 === 10 ? 0 : checksum % 11;
+ return checksum === digits[9];
+}
+
+/**
+ * Check if an input is a valid Canadian SIN (Social Insurance Number)
+ *
+ * The Social Insurance Number (SIN) is a 9 digit number that
+ * you need to work in Canada or to have access to government programs and benefits.
+ *
+ * https://en.wikipedia.org/wiki/Social_Insurance_Number
+ * https://www.canada.ca/en/employment-social-development/services/sin.html
+ * https://www.codercrunch.com/challenge/819302488/sin-validator
+ *
+ * @param {string} input
+ * @return {boolean}
+ */
+function isCanadianSIN(input) {
+ const digitsArray = input.split('');
+ const even = digitsArray
+ .filter((_, idx) => idx % 2)
+ .map(i => Number(i) * 2)
+ .join('')
+ .split('');
+
+ const total = digitsArray
+ .filter((_, idx) => !(idx % 2))
+ .concat(even)
+ .map(i => Number(i))
+ .reduce((acc, cur) => acc + cur);
+
+ return (total % 10 === 0);
+}
+
+/*
+ * cs-CZ validation function
+ * (Rodné číslo (RČ), persons only)
+ * Checks if birth date (first six digits) is valid and divisibility by 11
+ * Material not in DG TAXUD document sourced from:
+ * -`https://lorenc.info/3MA381/overeni-spravnosti-rodneho-cisla.htm`
+ * -`https://www.mvcr.cz/clanek/rady-a-sluzby-dokumenty-rodne-cislo.aspx`
+ */
+function csCzCheck(tin) {
+ tin = tin.replace(/\W/, '');
+
+ // Extract full year from TIN length
+ let full_year = parseInt(tin.slice(0, 2), 10);
+ if (tin.length === 10) {
+ if (full_year < 54) {
+ full_year = `20${full_year}`;
+ } else {
+ full_year = `19${full_year}`;
+ }
+ } else {
+ if (tin.slice(6) === '000') { return false; } // Three-zero serial not assigned before 1954
+ if (full_year < 54) {
+ full_year = `19${full_year}`;
+ } else {
+ return false; // No 18XX years seen in any of the resources
+ }
+ }
+ // Add missing zero if needed
+ if (full_year.length === 3) {
+ full_year = [full_year.slice(0, 2), '0', full_year.slice(2)].join('');
+ }
+
+ // Extract month from TIN and normalize
+ let month = parseInt(tin.slice(2, 4), 10);
+ if (month > 50) {
+ month -= 50;
+ }
+ if (month > 20) {
+ // Month-plus-twenty was only introduced in 2004
+ if (parseInt(full_year, 10) < 2004) { return false; }
+ month -= 20;
+ }
+ if (month < 10) { month = `0${month}`; }
+
+ // Check date validity
+ const date = `${full_year}/${month}/${tin.slice(4, 6)}`;
+ if (!isDate(date, 'YYYY/MM/DD')) { return false; }
+
+ // Verify divisibility by 11
+ if (tin.length === 10) {
+ if (parseInt(tin, 10) % 11 !== 0) {
+ // Some numbers up to and including 1985 are still valid if
+ // check (last) digit equals 0 and modulo of first 9 digits equals 10
+ const checkdigit = parseInt(tin.slice(0, 9), 10) % 11;
+ if (parseInt(full_year, 10) < 1986 && checkdigit === 10) {
+ if (parseInt(tin.slice(9), 10) !== 0) { return false; }
+ } else {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+/*
+ * de-AT validation function
+ * (Abgabenkontonummer, persons/entities)
+ * Verify TIN validity by calling luhnCheck()
+ */
+function deAtCheck(tin) {
+ return algorithms.luhnCheck(tin);
+}
+
+/*
+ * de-DE validation function
+ * (Steueridentifikationsnummer (Steuer-IdNr.), persons only)
+ * Tests for single duplicate/triplicate value, then calculates ISO 7064 check (last) digit
+ * Partial implementation of spec (same result with both algorithms always)
+ */
+function deDeCheck(tin) {
+ // Split digits into an array for further processing
+ const digits = tin.split('').map(a => parseInt(a, 10));
+
+ // Fill array with strings of number positions
+ let occurrences = [];
+ for (let i = 0; i < digits.length - 1; i++) {
+ occurrences.push('');
+ for (let j = 0; j < digits.length - 1; j++) {
+ if (digits[i] === digits[j]) {
+ occurrences[i] += j;
+ }
+ }
+ }
+
+ // Remove digits with one occurrence and test for only one duplicate/triplicate
+ occurrences = occurrences.filter(a => a.length > 1);
+ if (occurrences.length !== 2 && occurrences.length !== 3) { return false; }
+
+ // In case of triplicate value only two digits are allowed next to each other
+ if (occurrences[0].length === 3) {
+ const trip_locations = occurrences[0].split('').map(a => parseInt(a, 10));
+ let recurrent = 0; // Amount of neighbor occurrences
+ for (let i = 0; i < trip_locations.length - 1; i++) {
+ if (trip_locations[i] + 1 === trip_locations[i + 1]) {
+ recurrent += 1;
+ }
+ }
+ if (recurrent === 2) {
+ return false;
+ }
+ }
+ return algorithms.iso7064Check(tin);
+}
+
+/*
+ * dk-DK validation function
+ * (CPR-nummer (personnummer), persons only)
+ * Checks if birth date (first six digits) is valid and assigned to century (seventh) digit,
+ * and calculates check (last) digit
+ */
+function dkDkCheck(tin) {
+ tin = tin.replace(/\W/, '');
+
+ // Extract year, check if valid for given century digit and add century
+ let year = parseInt(tin.slice(4, 6), 10);
+ const century_digit = tin.slice(6, 7);
+ switch (century_digit) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ year = `19${year}`;
+ break;
+ case '4':
+ case '9':
+ if (year < 37) {
+ year = `20${year}`;
+ } else {
+ year = `19${year}`;
+ }
+ break;
+ default:
+ if (year < 37) {
+ year = `20${year}`;
+ } else if (year > 58) {
+ year = `18${year}`;
+ } else {
+ return false;
+ }
+ break;
+ }
+ // Add missing zero if needed
+ if (year.length === 3) {
+ year = [year.slice(0, 2), '0', year.slice(2)].join('');
+ }
+ // Check date validity
+ const date = `${year}/${tin.slice(2, 4)}/${tin.slice(0, 2)}`;
+ if (!isDate(date, 'YYYY/MM/DD')) { return false; }
+
+ // Split digits into an array for further processing
+ const digits = tin.split('').map(a => parseInt(a, 10));
+ let checksum = 0;
+ let weight = 4;
+ // Multiply by weight and add to checksum
+ for (let i = 0; i < 9; i++) {
+ checksum += digits[i] * weight;
+ weight -= 1;
+ if (weight === 1) {
+ weight = 7;
+ }
+ }
+ checksum %= 11;
+ if (checksum === 1) { return false; }
+ return checksum === 0 ? digits[9] === 0 : digits[9] === 11 - checksum;
+}
+
+/*
+ * el-CY validation function
+ * (Arithmos Forologikou Mitroou (AFM/ΑΦΜ), persons only)
+ * Verify TIN validity by calculating ASCII value of check (last) character
+ */
+function elCyCheck(tin) {
+ // split digits into an array for further processing
+ const digits = tin.slice(0, 8).split('').map(a => parseInt(a, 10));
+
+ let checksum = 0;
+ // add digits in even places
+ for (let i = 1; i < digits.length; i += 2) {
+ checksum += digits[i];
+ }
+
+ // add digits in odd places
+ for (let i = 0; i < digits.length; i += 2) {
+ if (digits[i] < 2) {
+ checksum += 1 - digits[i];
+ } else {
+ checksum += (2 * (digits[i] - 2)) + 5;
+ if (digits[i] > 4) {
+ checksum += 2;
+ }
+ }
+ }
+ return String.fromCharCode((checksum % 26) + 65) === tin.charAt(8);
+}
+
+/*
+ * el-GR validation function
+ * (Arithmos Forologikou Mitroou (AFM/ΑΦΜ), persons/entities)
+ * Verify TIN validity by calculating check (last) digit
+ * Algorithm not in DG TAXUD document- sourced from:
+ * - `http://epixeirisi.gr/%CE%9A%CE%A1%CE%99%CE%A3%CE%99%CE%9C%CE%91-%CE%98%CE%95%CE%9C%CE%91%CE%A4%CE%91-%CE%A6%CE%9F%CE%A1%CE%9F%CE%9B%CE%9F%CE%93%CE%99%CE%91%CE%A3-%CE%9A%CE%91%CE%99-%CE%9B%CE%9F%CE%93%CE%99%CE%A3%CE%A4%CE%99%CE%9A%CE%97%CE%A3/23791/%CE%91%CF%81%CE%B9%CE%B8%CE%BC%CF%8C%CF%82-%CE%A6%CE%BF%CF%81%CE%BF%CE%BB%CE%BF%CE%B3%CE%B9%CE%BA%CE%BF%CF%8D-%CE%9C%CE%B7%CF%84%CF%81%CF%8E%CE%BF%CF%85`
+ */
+function elGrCheck(tin) {
+ // split digits into an array for further processing
+ const digits = tin.split('').map(a => parseInt(a, 10));
+
+ let checksum = 0;
+ for (let i = 0; i < 8; i++) {
+ checksum += digits[i] * (2 ** (8 - i));
+ }
+ return ((checksum % 11) % 10) === digits[8];
+}
+
+/*
+ * en-GB validation function (should go here if needed)
+ * (National Insurance Number (NINO) or Unique Taxpayer Reference (UTR),
+ * persons/entities respectively)
+ */
+
+/*
+ * en-IE validation function
+ * (Personal Public Service Number (PPS No), persons only)
+ * Verify TIN validity by calculating check (second to last) character
+ */
+function enIeCheck(tin) {
+ let checksum = algorithms.reverseMultiplyAndSum(tin.split('').slice(0, 7).map(a => parseInt(a, 10)), 8);
+ if (tin.length === 9 && tin[8] !== 'W') {
+ checksum += (tin[8].charCodeAt(0) - 64) * 9;
+ }
+
+ checksum %= 23;
+ if (checksum === 0) {
+ return tin[7].toUpperCase() === 'W';
+ }
+ return tin[7].toUpperCase() === String.fromCharCode(64 + checksum);
+}
+
+// Valid US IRS campus prefixes
+const enUsCampusPrefix = {
+ andover: ['10', '12'],
+ atlanta: ['60', '67'],
+ austin: ['50', '53'],
+ brookhaven: ['01', '02', '03', '04', '05', '06', '11', '13', '14', '16', '21', '22', '23', '25', '34', '51', '52', '54', '55', '56', '57', '58', '59', '65'],
+ cincinnati: ['30', '32', '35', '36', '37', '38', '61'],
+ fresno: ['15', '24'],
+ internet: ['20', '26', '27', '45', '46', '47'],
+ kansas: ['40', '44'],
+ memphis: ['94', '95'],
+ ogden: ['80', '90'],
+ philadelphia: ['33', '39', '41', '42', '43', '46', '48', '62', '63', '64', '66', '68', '71', '72', '73', '74', '75', '76', '77', '81', '82', '83', '84', '85', '86', '87', '88', '91', '92', '93', '98', '99'],
+ sba: ['31'],
+};
+
+// Return an array of all US IRS campus prefixes
+function enUsGetPrefixes() {
+ const prefixes = [];
+
+ for (const location in enUsCampusPrefix) {
+ // https://github.com/gotwarlost/istanbul/blob/master/ignoring-code-for-coverage.md#ignoring-code-for-coverage-purposes
+ // istanbul ignore else
+ if (enUsCampusPrefix.hasOwnProperty(location)) {
+ prefixes.push(...enUsCampusPrefix[location]);
+ }
+ }
+
+ return prefixes;
+}
+
+/*
+ * en-US validation function
+ * Verify that the TIN starts with a valid IRS campus prefix
+ */
+function enUsCheck(tin) {
+ return enUsGetPrefixes().indexOf(tin.slice(0, 2)) !== -1;
+}
+
+/*
+ * es-AR validation function
+ * Clave Única de Identificación Tributaria (CUIT/CUIL)
+ * Sourced from:
+ * - https://servicioscf.afip.gob.ar/publico/abc/ABCpaso2.aspx?id_nivel1=3036&id_nivel2=3040&p=Conceptos%20b%C3%A1sicos
+ * - https://es.wikipedia.org/wiki/Clave_%C3%9Anica_de_Identificaci%C3%B3n_Tributaria
+ */
+
+function esArCheck(tin) {
+ let accum = 0;
+ let digits = tin.split('');
+ let digit = parseInt(digits.pop(), 10);
+ for (let i = 0; i < digits.length; i++) {
+ accum += digits[9 - i] * (2 + (i % 6));
+ }
+ let verif = 11 - (accum % 11);
+ if (verif === 11) {
+ verif = 0;
+ } else if (verif === 10) {
+ verif = 9;
+ }
+ return digit === verif;
+}
+
+/*
+ * es-ES validation function
+ * (Documento Nacional de Identidad (DNI)
+ * or Número de Identificación de Extranjero (NIE), persons only)
+ * Verify TIN validity by calculating check (last) character
+ */
+function esEsCheck(tin) {
+ // Split characters into an array for further processing
+ let chars = tin.toUpperCase().split('');
+
+ // Replace initial letter if needed
+ if (isNaN(parseInt(chars[0], 10)) && chars.length > 1) {
+ let lead_replace = 0;
+ switch (chars[0]) {
+ case 'Y':
+ lead_replace = 1;
+ break;
+ case 'Z':
+ lead_replace = 2;
+ break;
+ default:
+ }
+ chars.splice(0, 1, lead_replace);
+ // Fill with zeros if smaller than proper
+ } else {
+ while (chars.length < 9) {
+ chars.unshift(0);
+ }
+ }
+
+ // Calculate checksum and check according to lookup
+ const lookup = ['T', 'R', 'W', 'A', 'G', 'M', 'Y', 'F', 'P', 'D', 'X', 'B', 'N', 'J', 'Z', 'S', 'Q', 'V', 'H', 'L', 'C', 'K', 'E'];
+ chars = chars.join('');
+ let checksum = (parseInt(chars.slice(0, 8), 10) % 23);
+ return chars[8] === lookup[checksum];
+}
+
+/*
+ * et-EE validation function
+ * (Isikukood (IK), persons only)
+ * Checks if birth date (century digit and six following) is valid and calculates check (last) digit
+ * Material not in DG TAXUD document sourced from:
+ * - `https://www.oecd.org/tax/automatic-exchange/crs-implementation-and-assistance/tax-identification-numbers/Estonia-TIN.pdf`
+ */
+function etEeCheck(tin) {
+ // Extract year and add century
+ let full_year = tin.slice(1, 3);
+ const century_digit = tin.slice(0, 1);
+ switch (century_digit) {
+ case '1':
+ case '2':
+ full_year = `18${full_year}`;
+ break;
+ case '3':
+ case '4':
+ full_year = `19${full_year}`;
+ break;
+ default:
+ full_year = `20${full_year}`;
+ break;
+ }
+ // Check date validity
+ const date = `${full_year}/${tin.slice(3, 5)}/${tin.slice(5, 7)}`;
+ if (!isDate(date, 'YYYY/MM/DD')) { return false; }
+
+ // Split digits into an array for further processing
+ const digits = tin.split('').map(a => parseInt(a, 10));
+ let checksum = 0;
+ let weight = 1;
+ // Multiply by weight and add to checksum
+ for (let i = 0; i < 10; i++) {
+ checksum += digits[i] * weight;
+ weight += 1;
+ if (weight === 10) {
+ weight = 1;
+ }
+ }
+ // Do again if modulo 11 of checksum is 10
+ if (checksum % 11 === 10) {
+ checksum = 0;
+ weight = 3;
+ for (let i = 0; i < 10; i++) {
+ checksum += digits[i] * weight;
+ weight += 1;
+ if (weight === 10) {
+ weight = 1;
+ }
+ }
+ if (checksum % 11 === 10) { return digits[10] === 0; }
+ }
+
+ return checksum % 11 === digits[10];
+}
+
+/*
+ * fi-FI validation function
+ * (Henkilötunnus (HETU), persons only)
+ * Checks if birth date (first six digits plus century symbol) is valid
+ * and calculates check (last) digit
+ */
+function fiFiCheck(tin) {
+ // Extract year and add century
+ let full_year = tin.slice(4, 6);
+ const century_symbol = tin.slice(6, 7);
+ switch (century_symbol) {
+ case '+':
+ full_year = `18${full_year}`;
+ break;
+ case '-':
+ full_year = `19${full_year}`;
+ break;
+ default:
+ full_year = `20${full_year}`;
+ break;
+ }
+ // Check date validity
+ const date = `${full_year}/${tin.slice(2, 4)}/${tin.slice(0, 2)}`;
+ if (!isDate(date, 'YYYY/MM/DD')) { return false; }
+
+ // Calculate check character
+ let checksum = parseInt((tin.slice(0, 6) + tin.slice(7, 10)), 10) % 31;
+ if (checksum < 10) { return checksum === parseInt(tin.slice(10), 10); }
+
+ checksum -= 10;
+ const letters_lookup = ['A', 'B', 'C', 'D', 'E', 'F', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y'];
+ return letters_lookup[checksum] === tin.slice(10);
+}
+
+/*
+ * fr/nl-BE validation function
+ * (Numéro national (N.N.), persons only)
+ * Checks if birth date (first six digits) is valid and calculates check (last two) digits
+ */
+function frBeCheck(tin) {
+ // Zero month/day value is acceptable
+ if (tin.slice(2, 4) !== '00' || tin.slice(4, 6) !== '00') {
+ // Extract date from first six digits of TIN
+ const date = `${tin.slice(0, 2)}/${tin.slice(2, 4)}/${tin.slice(4, 6)}`;
+ if (!isDate(date, 'YY/MM/DD')) { return false; }
+ }
+
+ let checksum = 97 - (parseInt(tin.slice(0, 9), 10) % 97);
+ const checkdigits = parseInt(tin.slice(9, 11), 10);
+ if (checksum !== checkdigits) {
+ checksum = 97 - (parseInt(`2${tin.slice(0, 9)}`, 10) % 97);
+ if (checksum !== checkdigits) {
+ return false;
+ }
+ }
+ return true;
+}
+
+/*
+ * fr-FR validation function
+ * (Numéro fiscal de référence (numéro SPI), persons only)
+ * Verify TIN validity by calculating check (last three) digits
+ */
+function frFrCheck(tin) {
+ tin = tin.replace(/\s/g, '');
+ const checksum = parseInt(tin.slice(0, 10), 10) % 511;
+ const checkdigits = parseInt(tin.slice(10, 13), 10);
+ return checksum === checkdigits;
+}
+
+/*
+ * fr/lb-LU validation function
+ * (numéro d’identification personnelle, persons only)
+ * Verify birth date validity and run Luhn and Verhoeff checks
+ */
+function frLuCheck(tin) {
+ // Extract date and check validity
+ const date = `${tin.slice(0, 4)}/${tin.slice(4, 6)}/${tin.slice(6, 8)}`;
+ if (!isDate(date, 'YYYY/MM/DD')) { return false; }
+
+ // Run Luhn check
+ if (!algorithms.luhnCheck(tin.slice(0, 12))) { return false; }
+ // Remove Luhn check digit and run Verhoeff check
+ return algorithms.verhoeffCheck(`${tin.slice(0, 11)}${tin[12]}`);
+}
+
+/*
+ * hr-HR validation function
+ * (Osobni identifikacijski broj (OIB), persons/entities)
+ * Verify TIN validity by calling iso7064Check(digits)
+ */
+function hrHrCheck(tin) {
+ return algorithms.iso7064Check(tin);
+}
+
+/*
+ * hu-HU validation function
+ * (Adóazonosító jel, persons only)
+ * Verify TIN validity by calculating check (last) digit
+ */
+function huHuCheck(tin) {
+ // split digits into an array for further processing
+ const digits = tin.split('').map(a => parseInt(a, 10));
+
+ let checksum = 8;
+ for (let i = 1; i < 9; i++) {
+ checksum += digits[i] * (i + 1);
+ }
+ return checksum % 11 === digits[9];
+}
+
+/*
+ * lt-LT validation function (should go here if needed)
+ * (Asmens kodas, persons/entities respectively)
+ * Current validation check is alias of etEeCheck- same format applies
+ */
+
+/*
+ * it-IT first/last name validity check
+ * Accepts it-IT TIN-encoded names as a three-element character array and checks their validity
+ * Due to lack of clarity between resources ("Are only Italian consonants used?
+ * What happens if a person has X in their name?" etc.) only two test conditions
+ * have been implemented:
+ * Vowels may only be followed by other vowels or an X character
+ * and X characters after vowels may only be followed by other X characters.
+ */
+function itItNameCheck(name) {
+ // true at the first occurrence of a vowel
+ let vowelflag = false;
+
+ // true at the first occurrence of an X AFTER vowel
+ // (to properly handle last names with X as consonant)
+ let xflag = false;
+
+ for (let i = 0; i < 3; i++) {
+ if (!vowelflag && /[AEIOU]/.test(name[i])) {
+ vowelflag = true;
+ } else if (!xflag && vowelflag && (name[i] === 'X')) {
+ xflag = true;
+ } else if (i > 0) {
+ if (vowelflag && !xflag) {
+ if (!/[AEIOU]/.test(name[i])) { return false; }
+ }
+ if (xflag) {
+ if (!/X/.test(name[i])) { return false; }
+ }
+ }
+ }
+ return true;
+}
+
+/*
+ * it-IT validation function
+ * (Codice fiscale (TIN-IT), persons only)
+ * Verify name, birth date and codice catastale validity
+ * and calculate check character.
+ * Material not in DG-TAXUD document sourced from:
+ * `https://en.wikipedia.org/wiki/Italian_fiscal_code`
+ */
+function itItCheck(tin) {
+ // Capitalize and split characters into an array for further processing
+ const chars = tin.toUpperCase().split('');
+
+ // Check first and last name validity calling itItNameCheck()
+ if (!itItNameCheck(chars.slice(0, 3))) { return false; }
+ if (!itItNameCheck(chars.slice(3, 6))) { return false; }
+
+ // Convert letters in number spaces back to numbers if any
+ const number_locations = [6, 7, 9, 10, 12, 13, 14];
+ const number_replace = {
+ L: '0',
+ M: '1',
+ N: '2',
+ P: '3',
+ Q: '4',
+ R: '5',
+ S: '6',
+ T: '7',
+ U: '8',
+ V: '9',
+ };
+ for (const i of number_locations) {
+ if (chars[i] in number_replace) {
+ chars.splice(i, 1, number_replace[chars[i]]);
+ }
+ }
+
+ // Extract month and day, and check date validity
+ const month_replace = {
+ A: '01',
+ B: '02',
+ C: '03',
+ D: '04',
+ E: '05',
+ H: '06',
+ L: '07',
+ M: '08',
+ P: '09',
+ R: '10',
+ S: '11',
+ T: '12',
+ };
+ let month = month_replace[chars[8]];
+
+ let day = parseInt(chars[9] + chars[10], 10);
+ if (day > 40) { day -= 40; }
+ if (day < 10) { day = `0${day}`; }
+
+ const date = `${chars[6]}${chars[7]}/${month}/${day}`;
+ if (!isDate(date, 'YY/MM/DD')) { return false; }
+
+ // Calculate check character by adding up even and odd characters as numbers
+ let checksum = 0;
+ for (let i = 1; i < chars.length - 1; i += 2) {
+ let char_to_int = parseInt(chars[i], 10);
+ if (isNaN(char_to_int)) {
+ char_to_int = chars[i].charCodeAt(0) - 65;
+ }
+ checksum += char_to_int;
+ }
+
+ const odd_convert = { // Maps of characters at odd places
+ A: 1,
+ B: 0,
+ C: 5,
+ D: 7,
+ E: 9,
+ F: 13,
+ G: 15,
+ H: 17,
+ I: 19,
+ J: 21,
+ K: 2,
+ L: 4,
+ M: 18,
+ N: 20,
+ O: 11,
+ P: 3,
+ Q: 6,
+ R: 8,
+ S: 12,
+ T: 14,
+ U: 16,
+ V: 10,
+ W: 22,
+ X: 25,
+ Y: 24,
+ Z: 23,
+ 0: 1,
+ 1: 0,
+ };
+ for (let i = 0; i < chars.length - 1; i += 2) {
+ let char_to_int = 0;
+ if (chars[i] in odd_convert) {
+ char_to_int = odd_convert[chars[i]];
+ } else {
+ let multiplier = parseInt(chars[i], 10);
+ char_to_int = (2 * multiplier) + 1;
+ if (multiplier > 4) {
+ char_to_int += 2;
+ }
+ }
+ checksum += char_to_int;
+ }
+
+ if (String.fromCharCode(65 + (checksum % 26)) !== chars[15]) { return false; }
+ return true;
+}
+
+/*
+ * lv-LV validation function
+ * (Personas kods (PK), persons only)
+ * Check validity of birth date and calculate check (last) digit
+ * Support only for old format numbers (not starting with '32', issued before 2017/07/01)
+ * Material not in DG TAXUD document sourced from:
+ * `https://boot.ritakafija.lv/forums/index.php?/topic/88314-personas-koda-algoritms-%C4%8Deksumma/`
+ */
+function lvLvCheck(tin) {
+ tin = tin.replace(/\W/, '');
+ // Extract date from TIN
+ const day = tin.slice(0, 2);
+ if (day !== '32') { // No date/checksum check if new format
+ const month = tin.slice(2, 4);
+ if (month !== '00') { // No date check if unknown month
+ let full_year = tin.slice(4, 6);
+ switch (tin[6]) {
+ case '0':
+ full_year = `18${full_year}`;
+ break;
+ case '1':
+ full_year = `19${full_year}`;
+ break;
+ default:
+ full_year = `20${full_year}`;
+ break;
+ }
+ // Check date validity
+ const date = `${full_year}/${tin.slice(2, 4)}/${day}`;
+ if (!isDate(date, 'YYYY/MM/DD')) { return false; }
+ }
+
+ // Calculate check digit
+ let checksum = 1101;
+ const multip_lookup = [1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
+ for (let i = 0; i < tin.length - 1; i++) {
+ checksum -= parseInt(tin[i], 10) * multip_lookup[i];
+ }
+ return (parseInt(tin[10], 10) === checksum % 11);
+ }
+ return true;
+}
+
+/*
+ * mt-MT validation function
+ * (Identity Card Number or Unique Taxpayer Reference, persons/entities)
+ * Verify Identity Card Number structure (no other tests found)
+ */
+function mtMtCheck(tin) {
+ if (tin.length !== 9) { // No tests for UTR
+ let chars = tin.toUpperCase().split('');
+ // Fill with zeros if smaller than proper
+ while (chars.length < 8) {
+ chars.unshift(0);
+ }
+ // Validate format according to last character
+ switch (tin[7]) {
+ case 'A':
+ case 'P':
+ if (parseInt(chars[6], 10) === 0) { return false; }
+ break;
+ default: {
+ const first_part = parseInt(chars.join('').slice(0, 5), 10);
+ if (first_part > 32000) { return false; }
+ const second_part = parseInt(chars.join('').slice(5, 7), 10);
+ if (first_part === second_part) { return false; }
+ }
+ }
+ }
+ return true;
+}
+
+/*
+ * nl-NL validation function
+ * (Burgerservicenummer (BSN) or Rechtspersonen Samenwerkingsverbanden Informatie Nummer (RSIN),
+ * persons/entities respectively)
+ * Verify TIN validity by calculating check (last) digit (variant of MOD 11)
+ */
+function nlNlCheck(tin) {
+ return algorithms.reverseMultiplyAndSum(tin.split('').slice(0, 8).map(a => parseInt(a, 10)), 9) % 11 === parseInt(tin[8], 10);
+}
+
+/*
+ * pl-PL validation function
+ * (Powszechny Elektroniczny System Ewidencji Ludności (PESEL)
+ * or Numer identyfikacji podatkowej (NIP), persons/entities)
+ * Verify TIN validity by validating birth date (PESEL) and calculating check (last) digit
+ */
+function plPlCheck(tin) {
+ // NIP
+ if (tin.length === 10) {
+ // Calculate last digit by multiplying with lookup
+ const lookup = [6, 5, 7, 2, 3, 4, 5, 6, 7];
+ let checksum = 0;
+ for (let i = 0; i < lookup.length; i++) {
+ checksum += parseInt(tin[i], 10) * lookup[i];
+ }
+ checksum %= 11;
+ if (checksum === 10) { return false; }
+ return (checksum === parseInt(tin[9], 10));
+ }
+
+ // PESEL
+ // Extract full year using month
+ let full_year = tin.slice(0, 2);
+ let month = parseInt(tin.slice(2, 4), 10);
+ if (month > 80) {
+ full_year = `18${full_year}`;
+ month -= 80;
+ } else if (month > 60) {
+ full_year = `22${full_year}`;
+ month -= 60;
+ } else if (month > 40) {
+ full_year = `21${full_year}`;
+ month -= 40;
+ } else if (month > 20) {
+ full_year = `20${full_year}`;
+ month -= 20;
+ } else {
+ full_year = `19${full_year}`;
+ }
+ // Add leading zero to month if needed
+ if (month < 10) { month = `0${month}`; }
+ // Check date validity
+ const date = `${full_year}/${month}/${tin.slice(4, 6)}`;
+ if (!isDate(date, 'YYYY/MM/DD')) { return false; }
+
+ // Calculate last digit by multiplying with odd one-digit numbers except 5
+ let checksum = 0;
+ let multiplier = 1;
+ for (let i = 0; i < tin.length - 1; i++) {
+ checksum += (parseInt(tin[i], 10) * multiplier) % 10;
+ multiplier += 2;
+ if (multiplier > 10) {
+ multiplier = 1;
+ } else if (multiplier === 5) {
+ multiplier += 2;
+ }
+ }
+ checksum = 10 - (checksum % 10);
+ return checksum === parseInt(tin[10], 10);
+}
+
+/*
+* pt-BR validation function
+* (Cadastro de Pessoas Físicas (CPF, persons)
+* Cadastro Nacional de Pessoas Jurídicas (CNPJ, entities)
+* Both inputs will be validated
+*/
+
+function ptBrCheck(tin) {
+ if (tin.length === 11) {
+ let sum;
+ let remainder;
+ sum = 0;
+
+ if ( // Reject known invalid CPFs
+ tin === '11111111111' ||
+ tin === '22222222222' ||
+ tin === '33333333333' ||
+ tin === '44444444444' ||
+ tin === '55555555555' ||
+ tin === '66666666666' ||
+ tin === '77777777777' ||
+ tin === '88888888888' ||
+ tin === '99999999999' ||
+ tin === '00000000000'
+ ) return false;
+
+ for (let i = 1; i <= 9; i++) sum += parseInt(tin.substring(i - 1, i), 10) * (11 - i);
+ remainder = (sum * 10) % 11;
+ if (remainder === 10) remainder = 0;
+ if (remainder !== parseInt(tin.substring(9, 10), 10)) return false;
+ sum = 0;
+
+ for (let i = 1; i <= 10; i++) sum += parseInt(tin.substring(i - 1, i), 10) * (12 - i);
+ remainder = (sum * 10) % 11;
+ if (remainder === 10) remainder = 0;
+ if (remainder !== parseInt(tin.substring(10, 11), 10)) return false;
+
+ return true;
+ }
+
+ if ( // Reject know invalid CNPJs
+ tin === '00000000000000' ||
+ tin === '11111111111111' ||
+ tin === '22222222222222' ||
+ tin === '33333333333333' ||
+ tin === '44444444444444' ||
+ tin === '55555555555555' ||
+ tin === '66666666666666' ||
+ tin === '77777777777777' ||
+ tin === '88888888888888' ||
+ tin === '99999999999999') { return false; }
+
+ let length = tin.length - 2;
+ let identifiers = tin.substring(0, length);
+ let verificators = tin.substring(length);
+ let sum = 0;
+ let pos = length - 7;
+
+ for (let i = length; i >= 1; i--) {
+ sum += identifiers.charAt(length - i) * pos;
+ pos -= 1;
+ if (pos < 2) { pos = 9; }
+ }
+ let result = sum % 11 < 2 ? 0 : 11 - (sum % 11);
+ if (result !== parseInt(verificators.charAt(0), 10)) { return false; }
+
+ length += 1;
+ identifiers = tin.substring(0, length);
+ sum = 0;
+ pos = length - 7;
+ for (let i = length; i >= 1; i--) {
+ sum += identifiers.charAt(length - i) * pos;
+ pos -= 1;
+ if (pos < 2) { pos = 9; }
+ }
+ result = sum % 11 < 2 ? 0 : 11 - (sum % 11);
+ if (result !== parseInt(verificators.charAt(1), 10)) { return false; }
+
+ return true;
+}
+
+/*
+ * pt-PT validation function
+ * (Número de identificação fiscal (NIF), persons/entities)
+ * Verify TIN validity by calculating check (last) digit (variant of MOD 11)
+ */
+function ptPtCheck(tin) {
+ let checksum = 11 - (algorithms.reverseMultiplyAndSum(tin.split('').slice(0, 8).map(a => parseInt(a, 10)), 9) % 11);
+ if (checksum > 9) { return parseInt(tin[8], 10) === 0; }
+ return checksum === parseInt(tin[8], 10);
+}
+
+/*
+ * ro-RO validation function
+ * (Cod Numeric Personal (CNP) or Cod de înregistrare fiscală (CIF),
+ * persons only)
+ * Verify CNP validity by calculating check (last) digit (test not found for CIF)
+ * Material not in DG TAXUD document sourced from:
+ * `https://en.wikipedia.org/wiki/National_identification_number#Romania`
+ */
+function roRoCheck(tin) {
+ if (tin.slice(0, 4) !== '9000') { // No test found for this format
+ // Extract full year using century digit if possible
+ let full_year = tin.slice(1, 3);
+ switch (tin[0]) {
+ case '1':
+ case '2':
+ full_year = `19${full_year}`;
+ break;
+ case '3':
+ case '4':
+ full_year = `18${full_year}`;
+ break;
+ case '5':
+ case '6':
+ full_year = `20${full_year}`;
+ break;
+ default:
+ }
+
+ // Check date validity
+ const date = `${full_year}/${tin.slice(3, 5)}/${tin.slice(5, 7)}`;
+ if (date.length === 8) {
+ if (!isDate(date, 'YY/MM/DD')) { return false; }
+ } else if (!isDate(date, 'YYYY/MM/DD')) { return false; }
+
+ // Calculate check digit
+ const digits = tin.split('').map(a => parseInt(a, 10));
+ const multipliers = [2, 7, 9, 1, 4, 6, 3, 5, 8, 2, 7, 9];
+ let checksum = 0;
+ for (let i = 0; i < multipliers.length; i++) {
+ checksum += digits[i] * multipliers[i];
+ }
+ if (checksum % 11 === 10) { return digits[12] === 1; }
+ return digits[12] === checksum % 11;
+ }
+ return true;
+}
+
+/*
+ * sk-SK validation function
+ * (Rodné číslo (RČ) or bezvýznamové identifikačné číslo (BIČ), persons only)
+ * Checks validity of pre-1954 birth numbers (rodné číslo) only
+ * Due to the introduction of the pseudo-random BIČ it is not possible to test
+ * post-1954 birth numbers without knowing whether they are BIČ or RČ beforehand
+ */
+function skSkCheck(tin) {
+ if (tin.length === 9) {
+ tin = tin.replace(/\W/, '');
+ if (tin.slice(6) === '000') { return false; } // Three-zero serial not assigned before 1954
+
+ // Extract full year from TIN length
+ let full_year = parseInt(tin.slice(0, 2), 10);
+ if (full_year > 53) { return false; }
+ if (full_year < 10) {
+ full_year = `190${full_year}`;
+ } else {
+ full_year = `19${full_year}`;
+ }
+
+ // Extract month from TIN and normalize
+ let month = parseInt(tin.slice(2, 4), 10);
+ if (month > 50) {
+ month -= 50;
+ }
+ if (month < 10) { month = `0${month}`; }
+
+ // Check date validity
+ const date = `${full_year}/${month}/${tin.slice(4, 6)}`;
+ if (!isDate(date, 'YYYY/MM/DD')) { return false; }
+ }
+ return true;
+}
+
+/*
+ * sl-SI validation function
+ * (Davčna številka, persons/entities)
+ * Verify TIN validity by calculating check (last) digit (variant of MOD 11)
+ */
+function slSiCheck(tin) {
+ let checksum = 11 - (algorithms.reverseMultiplyAndSum(tin.split('').slice(0, 7).map(a => parseInt(a, 10)), 8) % 11);
+ if (checksum === 10) { return parseInt(tin[7], 10) === 0; }
+ return checksum === parseInt(tin[7], 10);
+}
+
+/*
+ * sv-SE validation function
+ * (Personnummer or samordningsnummer, persons only)
+ * Checks validity of birth date and calls luhnCheck() to validate check (last) digit
+ */
+function svSeCheck(tin) {
+ // Make copy of TIN and normalize to two-digit year form
+ let tin_copy = tin.slice(0);
+ if (tin.length > 11) {
+ tin_copy = tin_copy.slice(2);
+ }
+
+ // Extract date of birth
+ let full_year = '';
+ const month = tin_copy.slice(2, 4);
+ let day = parseInt(tin_copy.slice(4, 6), 10);
+ if (tin.length > 11) {
+ full_year = tin.slice(0, 4);
+ } else {
+ full_year = tin.slice(0, 2);
+ if (tin.length === 11 && day < 60) {
+ // Extract full year from centenarian symbol
+ // Should work just fine until year 10000 or so
+ let current_year = new Date().getFullYear().toString();
+ const current_century = parseInt(current_year.slice(0, 2), 10);
+ current_year = parseInt(current_year, 10);
+ if (tin[6] === '-') {
+ if (parseInt(`${current_century}${full_year}`, 10) > current_year) {
+ full_year = `${current_century - 1}${full_year}`;
+ } else {
+ full_year = `${current_century}${full_year}`;
+ }
+ } else {
+ full_year = `${current_century - 1}${full_year}`;
+ if (current_year - parseInt(full_year, 10) < 100) { return false; }
+ }
+ }
+ }
+
+ // Normalize day and check date validity
+ if (day > 60) { day -= 60; }
+ if (day < 10) { day = `0${day}`; }
+ const date = `${full_year}/${month}/${day}`;
+ if (date.length === 8) {
+ if (!isDate(date, 'YY/MM/DD')) { return false; }
+ } else if (!isDate(date, 'YYYY/MM/DD')) { return false; }
+
+ return algorithms.luhnCheck(tin.replace(/\W/, ''));
+}
+
+/**
+ * uk-UA validation function
+ * Verify TIN validity by calculating check (last) digit (variant of MOD 11)
+ */
+function ukUaCheck(tin) {
+ // Calculate check digit
+ const digits = tin.split('').map(a => parseInt(a, 10));
+ const multipliers = [-1, 5, 7, 9, 4, 6, 10, 5, 7];
+ let checksum = 0;
+ for (let i = 0; i < multipliers.length; i++) {
+ checksum += digits[i] * multipliers[i];
+ }
+ return checksum % 11 === 10 ? digits[9] === 0 : digits[9] === checksum % 11;
+}
+
+// Locale lookup objects
+
+/*
+ * Tax id regex formats for various locales
+ *
+ * Where not explicitly specified in DG-TAXUD document both
+ * uppercase and lowercase letters are acceptable.
+ */
+const taxIdFormat = {
+ 'bg-BG': /^\d{10}$/,
+ 'cs-CZ': /^\d{6}\/{0,1}\d{3,4}$/,
+ 'de-AT': /^\d{9}$/,
+ 'de-DE': /^[1-9]\d{10}$/,
+ 'dk-DK': /^\d{6}-{0,1}\d{4}$/,
+ 'el-CY': /^[09]\d{7}[A-Z]$/,
+ 'el-GR': /^([0-4]|[7-9])\d{8}$/,
+ 'en-CA': /^\d{9}$/,
+ 'en-GB': /^\d{10}$|^(?!GB|NK|TN|ZZ)(?![DFIQUV])[A-Z](?![DFIQUVO])[A-Z]\d{6}[ABCD ]$/i,
+ 'en-IE': /^\d{7}[A-W][A-IW]{0,1}$/i,
+ 'en-US': /^\d{2}[- ]{0,1}\d{7}$/,
+ 'es-AR': /(20|23|24|27|30|33|34)[0-9]{8}[0-9]/,
+ 'es-ES': /^(\d{0,8}|[XYZKLM]\d{7})[A-HJ-NP-TV-Z]$/i,
+ 'et-EE': /^[1-6]\d{6}(00[1-9]|0[1-9][0-9]|[1-6][0-9]{2}|70[0-9]|710)\d$/,
+ 'fi-FI': /^\d{6}[-+A]\d{3}[0-9A-FHJ-NPR-Y]$/i,
+ 'fr-BE': /^\d{11}$/,
+ 'fr-FR': /^[0-3]\d{12}$|^[0-3]\d\s\d{2}(\s\d{3}){3}$/, // Conforms both to official spec and provided example
+ 'fr-LU': /^\d{13}$/,
+ 'hr-HR': /^\d{11}$/,
+ 'hu-HU': /^8\d{9}$/,
+ 'it-IT': /^[A-Z]{6}[L-NP-V0-9]{2}[A-EHLMPRST][L-NP-V0-9]{2}[A-ILMZ][L-NP-V0-9]{3}[A-Z]$/i,
+ 'lv-LV': /^\d{6}-{0,1}\d{5}$/, // Conforms both to DG TAXUD spec and original research
+ 'mt-MT': /^\d{3,7}[APMGLHBZ]$|^([1-8])\1\d{7}$/i,
+ 'nl-NL': /^\d{9}$/,
+ 'pl-PL': /^\d{10,11}$/,
+ 'pt-BR': /(?:^\d{11}$)|(?:^\d{14}$)/,
+ 'pt-PT': /^\d{9}$/,
+ 'ro-RO': /^\d{13}$/,
+ 'sk-SK': /^\d{6}\/{0,1}\d{3,4}$/,
+ 'sl-SI': /^[1-9]\d{7}$/,
+ 'sv-SE': /^(\d{6}[-+]{0,1}\d{4}|(18|19|20)\d{6}[-+]{0,1}\d{4})$/,
+ 'uk-UA': /^\d{10}$/,
+};
+// taxIdFormat locale aliases
+taxIdFormat['lb-LU'] = taxIdFormat['fr-LU'];
+taxIdFormat['lt-LT'] = taxIdFormat['et-EE'];
+taxIdFormat['nl-BE'] = taxIdFormat['fr-BE'];
+taxIdFormat['fr-CA'] = taxIdFormat['en-CA'];
+
+// Algorithmic tax id check functions for various locales
+const taxIdCheck = {
+ 'bg-BG': bgBgCheck,
+ 'cs-CZ': csCzCheck,
+ 'de-AT': deAtCheck,
+ 'de-DE': deDeCheck,
+ 'dk-DK': dkDkCheck,
+ 'el-CY': elCyCheck,
+ 'el-GR': elGrCheck,
+ 'en-CA': isCanadianSIN,
+ 'en-IE': enIeCheck,
+ 'en-US': enUsCheck,
+ 'es-AR': esArCheck,
+ 'es-ES': esEsCheck,
+ 'et-EE': etEeCheck,
+ 'fi-FI': fiFiCheck,
+ 'fr-BE': frBeCheck,
+ 'fr-FR': frFrCheck,
+ 'fr-LU': frLuCheck,
+ 'hr-HR': hrHrCheck,
+ 'hu-HU': huHuCheck,
+ 'it-IT': itItCheck,
+ 'lv-LV': lvLvCheck,
+ 'mt-MT': mtMtCheck,
+ 'nl-NL': nlNlCheck,
+ 'pl-PL': plPlCheck,
+ 'pt-BR': ptBrCheck,
+ 'pt-PT': ptPtCheck,
+ 'ro-RO': roRoCheck,
+ 'sk-SK': skSkCheck,
+ 'sl-SI': slSiCheck,
+ 'sv-SE': svSeCheck,
+ 'uk-UA': ukUaCheck,
+};
+// taxIdCheck locale aliases
+taxIdCheck['lb-LU'] = taxIdCheck['fr-LU'];
+taxIdCheck['lt-LT'] = taxIdCheck['et-EE'];
+taxIdCheck['nl-BE'] = taxIdCheck['fr-BE'];
+taxIdCheck['fr-CA'] = taxIdCheck['en-CA'];
+
+// Regexes for locales where characters should be omitted before checking format
+const allsymbols = /[-\\\/!@#$%\^&\*\(\)\+\=\[\]]+/g;
+const sanitizeRegexes = {
+ 'de-AT': allsymbols,
+ 'de-DE': /[\/\\]/g,
+ 'fr-BE': allsymbols,
+};
+// sanitizeRegexes locale aliases
+sanitizeRegexes['nl-BE'] = sanitizeRegexes['fr-BE'];
+
+/*
+ * Validator function
+ * Return true if the passed string is a valid tax identification number
+ * for the specified locale.
+ * Throw an error exception if the locale is not supported.
+ */
+export default function isTaxID(str, locale = 'en-US') {
+ assertString(str);
+ // Copy TIN to avoid replacement if sanitized
+ let strcopy = str.slice(0);
+
+ if (locale in taxIdFormat) {
+ if (locale in sanitizeRegexes) {
+ strcopy = strcopy.replace(sanitizeRegexes[locale], '');
+ }
+ if (!taxIdFormat[locale].test(strcopy)) {
+ return false;
+ }
+
+ if (locale in taxIdCheck) {
+ return taxIdCheck[locale](strcopy);
+ }
+ // Fallthrough; not all locales have algorithmic checks
+ return true;
+ }
+ throw new Error(`Invalid locale '${locale}'`);
+}
diff --git a/src/lib/isTime.js b/src/lib/isTime.js
new file mode 100644
index 000000000..3169864c2
--- /dev/null
+++ b/src/lib/isTime.js
@@ -0,0 +1,25 @@
+import merge from './util/merge';
+
+const default_time_options = {
+ hourFormat: 'hour24',
+ mode: 'default',
+};
+
+const formats = {
+ hour24: {
+ default: /^([01]?[0-9]|2[0-3]):([0-5][0-9])$/,
+ withSeconds: /^([01]?[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$/,
+ withOptionalSeconds: /^([01]?[0-9]|2[0-3]):([0-5][0-9])(?::([0-5][0-9]))?$/,
+ },
+ hour12: {
+ default: /^(0?[1-9]|1[0-2]):([0-5][0-9]) (A|P)M$/,
+ withSeconds: /^(0?[1-9]|1[0-2]):([0-5][0-9]):([0-5][0-9]) (A|P)M$/,
+ withOptionalSeconds: /^(0?[1-9]|1[0-2]):([0-5][0-9])(?::([0-5][0-9]))? (A|P)M$/,
+ },
+};
+
+export default function isTime(input, options) {
+ options = merge(options, default_time_options);
+ if (typeof input !== 'string') return false;
+ return formats[options.hourFormat][options.mode].test(input);
+}
diff --git a/src/lib/isULID.js b/src/lib/isULID.js
new file mode 100644
index 000000000..2ace6f471
--- /dev/null
+++ b/src/lib/isULID.js
@@ -0,0 +1,6 @@
+import assertString from './util/assertString';
+
+export default function isULID(str) {
+ assertString(str);
+ return /^[0-7][0-9A-HJKMNP-TV-Z]{25}$/i.test(str);
+}
diff --git a/src/lib/isURL.js b/src/lib/isURL.js
index ea4e14778..b55f8e031 100644
--- a/src/lib/isURL.js
+++ b/src/lib/isURL.js
@@ -1,45 +1,80 @@
import assertString from './util/assertString';
+import checkHost from './util/checkHost';
+import includes from './util/includesString';
import isFQDN from './isFQDN';
import isIP from './isIP';
import merge from './util/merge';
+/*
+options for isURL method
+
+protocols - valid protocols can be modified with this option.
+require_tld - If set to false isURL will not check if the URL's host includes a top-level domain.
+require_protocol - if set to true isURL will return false if protocol is not present in the URL.
+require_host - if set to false isURL will not check if host is present in the URL.
+require_port - if set to true isURL will check if port is present in the URL.
+require_valid_protocol - isURL will check if the URL's protocol is present in the protocols option.
+allow_underscores - if set to true, the validator will allow underscores in the URL.
+host_whitelist - if set to an array of strings or regexp, and the domain matches none of the strings
+ defined in it, the validation fails.
+host_blacklist - if set to an array of strings or regexp, and the domain matches any of the strings
+ defined in it, the validation fails.
+allow_trailing_dot - if set to true, the validator will allow the domain to end with
+ a `.` character.
+allow_protocol_relative_urls - if set to true protocol relative URLs will be allowed.
+allow_fragments - if set to false isURL will return false if fragments are present.
+allow_query_components - if set to false isURL will return false if query components are present.
+disallow_auth - if set to true, the validator will fail if the URL contains an authentication
+ component, e.g. `http://username:password@example.com`
+validate_length - if set to false isURL will skip string length validation. `max_allowed_length`
+ will be ignored if this is set as `false`.
+max_allowed_length - if set, isURL will not allow URLs longer than the specified value (default is
+ 2084 that IE maximum URL length).
+
+*/
+
+
const default_url_options = {
protocols: ['http', 'https', 'ftp'],
require_tld: true,
require_protocol: false,
require_host: true,
+ require_port: false,
require_valid_protocol: true,
allow_underscores: false,
allow_trailing_dot: false,
allow_protocol_relative_urls: false,
+ allow_fragments: true,
+ allow_query_components: true,
+ validate_length: true,
+ max_allowed_length: 2084,
};
const wrapped_ipv6 = /^\[([^\]]+)\](?::([0-9]+))?$/;
-function isRegExp(obj) {
- return Object.prototype.toString.call(obj) === '[object RegExp]';
-}
-
-function checkHost(host, matches) {
- for (let i = 0; i < matches.length; i++) {
- let match = matches[i];
- if (host === match || (isRegExp(match) && match.test(host))) {
- return true;
- }
- }
- return false;
-}
-
export default function isURL(url, options) {
assertString(url);
- if (!url || url.length >= 2083 || /[\s<>]/.test(url)) {
+ if (!url || /[\s<>]/.test(url)) {
return false;
}
if (url.indexOf('mailto:') === 0) {
return false;
}
options = merge(options, default_url_options);
+
+ if (options.validate_length && url.length > options.max_allowed_length) {
+ return false;
+ }
+
+ if (!options.allow_fragments && includes(url, '#')) {
+ return false;
+ }
+
+ if (!options.allow_query_components && (includes(url, '?') || includes(url, '&'))) {
+ return false;
+ }
+
let protocol, auth, host, hostname, port, port_str, split, ipv6;
split = url.split('#');
@@ -48,18 +83,107 @@ export default function isURL(url, options) {
split = url.split('?');
url = split.shift();
- split = url.split('://');
- if (split.length > 1) {
- protocol = split.shift();
+ // Replaced the 'split("://")' logic with a regex to match the protocol.
+ // This correctly identifies schemes like `javascript:` which don't use `//`.
+ // However, we need to be careful not to confuse authentication credentials (user:password@host)
+ // with protocols. A colon before an @ symbol might be part of auth, not a protocol separator.
+ const protocol_match = url.match(/^([a-z][a-z0-9+\-.]*):/i);
+ let had_explicit_protocol = false;
+
+ const cleanUpProtocol = (potential_protocol) => {
+ had_explicit_protocol = true;
+ protocol = potential_protocol.toLowerCase();
+
if (options.require_valid_protocol && options.protocols.indexOf(protocol) === -1) {
+ // The identified protocol is not in the allowed list.
return false;
}
+
+ // Remove the protocol from the URL string.
+ return url.substring(protocol_match[0].length);
+ };
+
+ if (protocol_match) {
+ const potential_protocol = protocol_match[1];
+ const after_colon = url.substring(protocol_match[0].length);
+
+ // Check if what follows looks like authentication credentials (user:password@host)
+ // rather than a protocol. This happens when:
+ // 1. There's no `//` after the colon (protocols like `http://` have this)
+ // 2. There's an `@` symbol before any `/`
+ // 3. The part before `@` contains only valid auth characters (alphanumeric, -, _, ., %, :)
+ const starts_with_slashes = after_colon.slice(0, 2) === '//';
+
+ if (!starts_with_slashes) {
+ const first_slash_position = after_colon.indexOf('/');
+ const before_slash = first_slash_position === -1
+ ? after_colon
+ : after_colon.substring(0, first_slash_position);
+ const at_position = before_slash.indexOf('@');
+
+ if (at_position !== -1) {
+ const before_at = before_slash.substring(0, at_position);
+ const valid_auth_regex = /^[a-zA-Z0-9\-_.%:]*$/;
+ const is_valid_auth = valid_auth_regex.test(before_at);
+
+ if (is_valid_auth) {
+ // This looks like authentication (e.g., user:password@host), not a protocol
+ if (options.require_protocol) {
+ return false;
+ }
+
+ // Don't consume the colon; let the auth parsing handle it later
+ } else {
+ // This looks like a malicious protocol (e.g., javascript:alert();@host)
+ url = cleanUpProtocol(potential_protocol);
+
+ if (url === false) {
+ return false;
+ }
+ }
+ } else {
+ // No @ symbol found. Check if this could be a port number instead of a protocol.
+ // If what's after the colon is numeric (or starts with a digit and contains only
+ // valid port characters until a path separator), it's likely hostname:port, not a protocol.
+ const looks_like_port = /^[0-9]/.test(after_colon);
+
+ if (looks_like_port) {
+ // This looks like hostname:port, not a protocol
+ if (options.require_protocol) {
+ return false;
+ }
+ // Don't consume anything; let it be parsed as hostname:port
+ } else {
+ // This is definitely a protocol
+ url = cleanUpProtocol(potential_protocol);
+
+ if (url === false) {
+ return false;
+ }
+ }
+ }
+ } else {
+ // Starts with '//', this is definitely a protocol like http://
+ url = cleanUpProtocol(potential_protocol);
+
+ if (url === false) {
+ return false;
+ }
+ }
} else if (options.require_protocol) {
return false;
- } else if (options.allow_protocol_relative_urls && url.substr(0, 2) === '//') {
- split[0] = url.substr(2);
}
- url = split.join('://');
+
+ // Handle leading '//' only as protocol-relative when there was NO explicit protocol.
+ // If there was an explicit protocol, '//' is the normal separator
+ // and should be stripped unconditionally.
+ if (url.slice(0, 2) === '//') {
+ if (!had_explicit_protocol && !options.allow_protocol_relative_urls) {
+ return false;
+ }
+
+ url = url.slice(2);
+ }
if (url === '') {
return false;
@@ -74,10 +198,20 @@ export default function isURL(url, options) {
split = url.split('@');
if (split.length > 1) {
+ if (options.disallow_auth) {
+ return false;
+ }
+ if (split[0] === '') {
+ return false;
+ }
auth = split.shift();
if (auth.indexOf(':') >= 0 && auth.split(':').length > 2) {
return false;
}
+ const [user, password] = auth.split(':');
+ if (user === '' && password === '') {
+ return false;
+ }
}
hostname = split.join('@');
@@ -96,11 +230,21 @@ export default function isURL(url, options) {
}
}
- if (port_str !== null) {
+ if (port_str !== null && port_str.length > 0) {
port = parseInt(port_str, 10);
if (!/^[0-9]+$/.test(port_str) || port <= 0 || port > 65535) {
return false;
}
+ } else if (options.require_port) {
+ return false;
+ }
+
+ if (options.host_whitelist) {
+ return checkHost(host, options.host_whitelist);
+ }
+
+ if (host === '' && !options.require_host) {
+ return true;
}
if (!isIP(host) && !isFQDN(host, options) && (!ipv6 || !isIP(ipv6, 6))) {
@@ -109,9 +253,6 @@ export default function isURL(url, options) {
host = host || ipv6;
- if (options.host_whitelist && !checkHost(host, options.host_whitelist)) {
- return false;
- }
if (options.host_blacklist && checkHost(host, options.host_blacklist)) {
return false;
}
diff --git a/src/lib/isUUID.js b/src/lib/isUUID.js
index 61d938ac3..9d6040f04 100644
--- a/src/lib/isUUID.js
+++ b/src/lib/isUUID.js
@@ -1,14 +1,29 @@
import assertString from './util/assertString';
const uuid = {
- 3: /^[0-9A-F]{8}-[0-9A-F]{4}-3[0-9A-F]{3}-[0-9A-F]{4}-[0-9A-F]{12}$/i,
+ 1: /^[0-9A-F]{8}-[0-9A-F]{4}-1[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i,
+ 2: /^[0-9A-F]{8}-[0-9A-F]{4}-2[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i,
+ 3: /^[0-9A-F]{8}-[0-9A-F]{4}-3[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i,
4: /^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i,
5: /^[0-9A-F]{8}-[0-9A-F]{4}-5[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i,
- all: /^[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}$/i,
+ 6: /^[0-9A-F]{8}-[0-9A-F]{4}-6[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i,
+ 7: /^[0-9A-F]{8}-[0-9A-F]{4}-7[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i,
+ 8: /^[0-9A-F]{8}-[0-9A-F]{4}-8[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i,
+
+ nil: /^00000000-0000-0000-0000-000000000000$/i,
+ max: /^ffffffff-ffff-ffff-ffff-ffffffffffff$/i,
+ loose: /^[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}$/i,
+
+ // From https://github.com/uuidjs/uuid/blob/main/src/regex.js
+ all: /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/i,
};
-export default function isUUID(str, version = 'all') {
+export default function isUUID(str, version) {
assertString(str);
- const pattern = uuid[version];
- return pattern && pattern.test(str);
+
+ if (version === undefined || version === null) {
+ version = 'all';
+ }
+
+ return version in uuid ? uuid[version].test(str) : false;
}
diff --git a/src/lib/isVAT.js b/src/lib/isVAT.js
new file mode 100644
index 000000000..1ec2c5991
--- /dev/null
+++ b/src/lib/isVAT.js
@@ -0,0 +1,140 @@
+import assertString from './util/assertString';
+import * as algorithms from './util/algorithms';
+
+const AU = (str) => {
+ const match = str.match(/^(AU)?(\d{11})$/);
+ if (!match) {
+ return false;
+ }
+ // @see {@link https://abr.business.gov.au/Help/AbnFormat}
+ const weights = [10, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19];
+ str = str.replace(/^AU/, '');
+ const ABN = (parseInt(str.slice(0, 1), 10) - 1).toString() + str.slice(1);
+ let total = 0;
+ for (let i = 0; i < 11; i++) {
+ total += weights[i] * ABN.charAt(i);
+ }
+ return (total !== 0 && total % 89 === 0);
+};
+
+const CH = (str) => {
+ // @see {@link https://www.ech.ch/de/ech/ech-0097/5.2.0}
+ const hasValidCheckNumber = (digits) => {
+ const lastDigit = digits.pop(); // used as check number
+ const weights = [5, 4, 3, 2, 7, 6, 5, 4];
+ const calculatedCheckNumber = (11 - (digits.reduce((acc, el, idx) =>
+ acc + (el * weights[idx]), 0) % 11)) % 11;
+
+ return lastDigit === calculatedCheckNumber;
+ };
+
+ // @see {@link https://www.estv.admin.ch/estv/de/home/mehrwertsteuer/uid/mwst-uid-nummer.html}
+ return /^(CHE[- ]?)?(\d{9}|(\d{3}\.\d{3}\.\d{3})|(\d{3} \d{3} \d{3})) ?(TVA|MWST|IVA)?$/.test(str) && hasValidCheckNumber((str.match(/\d/g).map(el => +el)));
+};
+
+const PT = (str) => {
+ const match = str.match(/^(PT)?(\d{9})$/);
+ if (!match) {
+ return false;
+ }
+
+ const tin = match[2];
+
+ const checksum = 11 - (algorithms.reverseMultiplyAndSum(tin.split('').slice(0, 8).map(a => parseInt(a, 10)), 9) % 11);
+ if (checksum > 9) {
+ return parseInt(tin[8], 10) === 0;
+ }
+ return checksum === parseInt(tin[8], 10);
+};
+
+export const vatMatchers = {
+ /**
+ * European Union VAT identification numbers
+ */
+ AT: str => /^(AT)?U\d{8}$/.test(str),
+ BE: str => /^(BE)?\d{10}$/.test(str),
+ BG: str => /^(BG)?\d{9,10}$/.test(str),
+ HR: str => /^(HR)?\d{11}$/.test(str),
+ CY: str => /^(CY)?\w{9}$/.test(str),
+ CZ: str => /^(CZ)?\d{8,10}$/.test(str),
+ DK: str => /^(DK)?\d{8}$/.test(str),
+ EE: str => /^(EE)?\d{9}$/.test(str),
+ FI: str => /^(FI)?\d{8}$/.test(str),
+ FR: str => /^(FR)([A-Z0-9]{2}\d{9})$/.test(str),
+ DE: str => /^(DE)?\d{9}$/.test(str),
+ EL: str => /^(EL)?\d{9}$/.test(str),
+ HU: str => /^(HU)?\d{8}$/.test(str),
+ IE: str => /^(IE)?\d{7}\w{1}(W)?$/.test(str),
+ IT: str => /^(IT)?\d{11}$/.test(str),
+ LV: str => /^(LV)?\d{11}$/.test(str),
+ LT: str => /^(LT)?\d{9,12}$/.test(str),
+ LU: str => /^(LU)?\d{8}$/.test(str),
+ MT: str => /^(MT)?\d{8}$/.test(str),
+ NL: str => /^(NL)?\d{9}B\d{2}$/.test(str),
+ PL: str => /^(PL)?(\d{10}|(\d{3}-\d{3}-\d{2}-\d{2})|(\d{3}-\d{2}-\d{2}-\d{3}))$/.test(str),
+ PT,
+ RO: str => /^(RO)?\d{2,10}$/.test(str),
+ SK: str => /^(SK)?\d{10}$/.test(str),
+ SI: str => /^(SI)?\d{8}$/.test(str),
+ ES: str => /^(ES)?\w\d{7}[A-Z]$/.test(str),
+ SE: str => /^(SE)?\d{12}$/.test(str),
+
+ /**
+ * VAT numbers of non-EU countries
+ */
+ AL: str => /^(AL)?\w{9}[A-Z]$/.test(str),
+ MK: str => /^(MK)?\d{13}$/.test(str),
+ AU,
+ BY: str => /^(УНП )?\d{9}$/.test(str),
+ CA: str => /^(CA)?\d{9}$/.test(str),
+ IS: str => /^(IS)?\d{5,6}$/.test(str),
+ IN: str => /^(IN)?\d{15}$/.test(str),
+ ID: str => /^(ID)?(\d{15}|(\d{2}.\d{3}.\d{3}.\d{1}-\d{3}.\d{3}))$/.test(str),
+ IL: str => /^(IL)?\d{9}$/.test(str),
+ KZ: str => /^(KZ)?\d{12}$/.test(str),
+ NZ: str => /^(NZ)?\d{9}$/.test(str),
+ NG: str => /^(NG)?(\d{12}|(\d{8}-\d{4}))$/.test(str),
+ NO: str => /^(NO)?\d{9}MVA$/.test(str),
+ PH: str => /^(PH)?(\d{12}|\d{3} \d{3} \d{3} \d{3})$/.test(str),
+ RU: str => /^(RU)?(\d{10}|\d{12})$/.test(str),
+ SM: str => /^(SM)?\d{5}$/.test(str),
+ SA: str => /^(SA)?\d{15}$/.test(str),
+ RS: str => /^(RS)?\d{9}$/.test(str),
+ CH,
+ TR: str => /^(TR)?\d{10}$/.test(str),
+ UA: str => /^(UA)?\d{12}$/.test(str),
+ GB: str => /^GB((\d{3} \d{4} ([0-8][0-9]|9[0-6]))|(\d{9} \d{3})|(((GD[0-4])|(HA[5-9]))[0-9]{2}))$/.test(str),
+ UZ: str => /^(UZ)?\d{9}$/.test(str),
+
+ /**
+ * VAT numbers of Latin American countries
+ */
+ AR: str => /^(AR)?\d{11}$/.test(str),
+ BO: str => /^(BO)?\d{7}$/.test(str),
+ BR: str => /^(BR)?((\d{2}.\d{3}.\d{3}\/\d{4}-\d{2})|(\d{3}.\d{3}.\d{3}-\d{2}))$/.test(str),
+ CL: str => /^(CL)?\d{8}-\d{1}$/.test(str),
+ CO: str => /^(CO)?\d{10}$/.test(str),
+ CR: str => /^(CR)?\d{9,12}$/.test(str),
+ EC: str => /^(EC)?\d{13}$/.test(str),
+ SV: str => /^(SV)?\d{4}-\d{6}-\d{3}-\d{1}$/.test(str),
+ GT: str => /^(GT)?\d{7}-\d{1}$/.test(str),
+ HN: str => /^(HN)?$/.test(str),
+ MX: str => /^(MX)?\w{3,4}\d{6}\w{3}$/.test(str),
+ NI: str => /^(NI)?\d{3}-\d{6}-\d{4}\w{1}$/.test(str),
+ PA: str => /^(PA)?$/.test(str),
+ PY: str => /^(PY)?\d{6,8}-\d{1}$/.test(str),
+ PE: str => /^(PE)?\d{11}$/.test(str),
+ DO: str => /^(DO)?(\d{11}|(\d{3}-\d{7}-\d{1})|[1,4,5]{1}\d{8}|([1,4,5]{1})-\d{2}-\d{5}-\d{1})$/.test(str),
+ UY: str => /^(UY)?\d{12}$/.test(str),
+ VE: str => /^(VE)?[J,G,V,E]{1}-(\d{9}|(\d{8}-\d{1}))$/.test(str),
+};
+
+export default function isVAT(str, countryCode) {
+ assertString(str);
+ assertString(countryCode);
+
+ if (countryCode in vatMatchers) {
+ return vatMatchers[countryCode](str);
+ }
+ throw new Error(`Invalid country code: '${countryCode}'`);
+}
diff --git a/src/lib/ltrim.js b/src/lib/ltrim.js
index 7f1ee11cd..372d2df8e 100644
--- a/src/lib/ltrim.js
+++ b/src/lib/ltrim.js
@@ -2,6 +2,7 @@ import assertString from './util/assertString';
export default function ltrim(str, chars) {
assertString(str);
- const pattern = chars ? new RegExp(`^[${chars}]+`, 'g') : /^\s+/g;
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#Escaping
+ const pattern = chars ? new RegExp(`^[${chars.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}]+`, 'g') : /^\s+/g;
return str.replace(pattern, '');
}
diff --git a/src/lib/matches.js b/src/lib/matches.js
index 5b435e2af..9e23c2e46 100644
--- a/src/lib/matches.js
+++ b/src/lib/matches.js
@@ -5,5 +5,5 @@ export default function matches(str, pattern, modifiers) {
if (Object.prototype.toString.call(pattern) !== '[object RegExp]') {
pattern = new RegExp(pattern, modifiers);
}
- return pattern.test(str);
+ return !!str.match(pattern);
}
diff --git a/src/lib/normalizeEmail.js b/src/lib/normalizeEmail.js
index e69d39054..ceb252f34 100644
--- a/src/lib/normalizeEmail.js
+++ b/src/lib/normalizeEmail.js
@@ -29,6 +29,12 @@ const default_normalize_email_options = {
// Removes the subaddress (e.g. "-foo") from the email address
yahoo_remove_subaddress: true,
+ // The following conversions are specific to Yandex
+ // Lowercases the local part of the Yandex address (known to be case-insensitive)
+ yandex_lowercase: true,
+ // all yandex domains are equal, this explicitly sets the domain to 'yandex.ru'
+ yandex_convert_yandexru: true,
+
// The following conversions are specific to iCloud
// Lowercases the local part of the iCloud address (known to be case-insensitive)
icloud_lowercase: true,
@@ -145,6 +151,24 @@ const yahoo_domains = [
'ymail.com',
];
+// List of domains used by yandex.ru
+const yandex_domains = [
+ 'yandex.ru',
+ 'yandex.ua',
+ 'yandex.kz',
+ 'yandex.com',
+ 'yandex.by',
+ 'ya.ru',
+];
+
+// replace single dots, but not multiple consecutive dots
+function dotsReplacer(match) {
+ if (match.length > 1) {
+ return match;
+ }
+ return '';
+}
+
export default function normalizeEmail(email, options) {
options = merge(options, default_normalize_email_options);
@@ -162,7 +186,8 @@ export default function normalizeEmail(email, options) {
parts[0] = parts[0].split('+')[0];
}
if (options.gmail_remove_dots) {
- parts[0] = parts[0].replace(/\./g, '');
+ // this does not replace consecutive dots like example..email@gmail.com
+ parts[0] = parts[0].replace(/\.+/g, dotsReplacer);
}
if (!parts[0].length) {
return false;
@@ -171,7 +196,7 @@ export default function normalizeEmail(email, options) {
parts[0] = parts[0].toLowerCase();
}
parts[1] = options.gmail_convert_googlemaildotcom ? 'gmail.com' : parts[1];
- } else if (~icloud_domains.indexOf(parts[1])) {
+ } else if (icloud_domains.indexOf(parts[1]) >= 0) {
// Address is iCloud
if (options.icloud_remove_subaddress) {
parts[0] = parts[0].split('+')[0];
@@ -182,7 +207,7 @@ export default function normalizeEmail(email, options) {
if (options.all_lowercase || options.icloud_lowercase) {
parts[0] = parts[0].toLowerCase();
}
- } else if (~outlookdotcom_domains.indexOf(parts[1])) {
+ } else if (outlookdotcom_domains.indexOf(parts[1]) >= 0) {
// Address is Outlook.com
if (options.outlookdotcom_remove_subaddress) {
parts[0] = parts[0].split('+')[0];
@@ -193,7 +218,7 @@ export default function normalizeEmail(email, options) {
if (options.all_lowercase || options.outlookdotcom_lowercase) {
parts[0] = parts[0].toLowerCase();
}
- } else if (~yahoo_domains.indexOf(parts[1])) {
+ } else if (yahoo_domains.indexOf(parts[1]) >= 0) {
// Address is Yahoo
if (options.yahoo_remove_subaddress) {
let components = parts[0].split('-');
@@ -205,6 +230,11 @@ export default function normalizeEmail(email, options) {
if (options.all_lowercase || options.yahoo_lowercase) {
parts[0] = parts[0].toLowerCase();
}
+ } else if (yandex_domains.indexOf(parts[1]) >= 0) {
+ if (options.all_lowercase || options.yandex_lowercase) {
+ parts[0] = parts[0].toLowerCase();
+ }
+ parts[1] = options.yandex_convert_yandexru ? 'yandex.ru' : parts[1];
} else if (options.all_lowercase) {
// Any other address
parts[0] = parts[0].toLowerCase();
diff --git a/src/lib/rtrim.js b/src/lib/rtrim.js
index 9045bb72e..2d311574b 100644
--- a/src/lib/rtrim.js
+++ b/src/lib/rtrim.js
@@ -2,12 +2,16 @@ import assertString from './util/assertString';
export default function rtrim(str, chars) {
assertString(str);
- const pattern = chars ? new RegExp(`[${chars}]`) : /\s/;
-
- let idx = str.length - 1;
- while (idx >= 0 && pattern.test(str[idx])) {
- idx--;
+ if (chars) {
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#Escaping
+ const pattern = new RegExp(`[${chars.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}]+$`, 'g');
+ return str.replace(pattern, '');
+ }
+ // Use a faster and more safe than regex trim method https://blog.stevenlevithan.com/archives/faster-trim-javascript
+ let strIndex = str.length - 1;
+ while (/\s/.test(str.charAt(strIndex))) {
+ strIndex -= 1;
}
- return idx < str.length ? str.substr(0, idx + 1) : str;
+ return str.slice(0, strIndex + 1);
}
diff --git a/src/lib/toBoolean.js b/src/lib/toBoolean.js
index b5d5f292b..95e210ca3 100644
--- a/src/lib/toBoolean.js
+++ b/src/lib/toBoolean.js
@@ -3,7 +3,7 @@ import assertString from './util/assertString';
export default function toBoolean(str, strict) {
assertString(str);
if (strict) {
- return str === '1' || str === 'true';
+ return str === '1' || /^true$/i.test(str);
}
- return str !== '0' && str !== 'false' && str !== '';
+ return str !== '0' && !/^false$/i.test(str) && str !== '';
}
diff --git a/src/lib/toDate.js b/src/lib/toDate.js
index 7cf80510e..179645fda 100644
--- a/src/lib/toDate.js
+++ b/src/lib/toDate.js
@@ -2,6 +2,7 @@ import assertString from './util/assertString';
export default function toDate(date) {
assertString(date);
+
date = Date.parse(date);
return !isNaN(date) ? new Date(date) : null;
}
diff --git a/src/lib/toFloat.js b/src/lib/toFloat.js
index eb3eb5a33..fa7d7b7c7 100644
--- a/src/lib/toFloat.js
+++ b/src/lib/toFloat.js
@@ -1,6 +1,7 @@
-import assertString from './util/assertString';
+import isFloat from './isFloat';
export default function toFloat(str) {
- assertString(str);
+ if (!isFloat(str)) return NaN;
+
return parseFloat(str);
}
diff --git a/src/lib/unescape.js b/src/lib/unescape.js
index 213a0f70b..feb255ac0 100644
--- a/src/lib/unescape.js
+++ b/src/lib/unescape.js
@@ -2,12 +2,15 @@ import assertString from './util/assertString';
export default function unescape(str) {
assertString(str);
- return (str.replace(/&/g, '&')
- .replace(/"/g, '"')
+ return (str.replace(/"/g, '"')
.replace(/'/g, "'")
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(///g, '/')
.replace(/\/g, '\\')
- .replace(/`/g, '`'));
+ .replace(/`/g, '`')
+ .replace(/&/g, '&'));
+ // & replacement has to be the last one to prevent
+ // bugs with intermediate strings containing escape sequences
+ // See: https://github.com/validatorjs/validator.js/issues/1827
}
diff --git a/src/lib/util/algorithms.js b/src/lib/util/algorithms.js
new file mode 100644
index 000000000..4ac1f2784
--- /dev/null
+++ b/src/lib/util/algorithms.js
@@ -0,0 +1,97 @@
+/**
+ * Algorithmic validation functions
+ * May be used as is or implemented in the workflow of other validators.
+ */
+
+/*
+ * ISO 7064 validation function
+ * Called with a string of numbers (incl. check digit)
+ * to validate according to ISO 7064 (MOD 11, 10).
+ */
+export function iso7064Check(str) {
+ let checkvalue = 10;
+ for (let i = 0; i < str.length - 1; i++) {
+ checkvalue = (parseInt(str[i], 10) + checkvalue) % 10 === 0 ? (10 * 2) % 11 :
+ (((parseInt(str[i], 10) + checkvalue) % 10) * 2) % 11;
+ }
+ checkvalue = checkvalue === 1 ? 0 : 11 - checkvalue;
+ return checkvalue === parseInt(str[10], 10);
+}
+
+/*
+ * Luhn (mod 10) validation function
+ * Called with a string of numbers (incl. check digit)
+ * to validate according to the Luhn algorithm.
+ */
+export function luhnCheck(str) {
+ let checksum = 0;
+ let second = false;
+ for (let i = str.length - 1; i >= 0; i--) {
+ if (second) {
+ const product = parseInt(str[i], 10) * 2;
+ if (product > 9) {
+ // sum digits of product and add to checksum
+ checksum += product.toString().split('').map(a => parseInt(a, 10)).reduce((a, b) => a + b, 0);
+ } else {
+ checksum += product;
+ }
+ } else {
+ checksum += parseInt(str[i], 10);
+ }
+ second = !second;
+ }
+ return checksum % 10 === 0;
+}
+
+/*
+ * Reverse TIN multiplication and summation helper function
+ * Called with an array of single-digit integers and a base multiplier
+ * to calculate the sum of the digits multiplied in reverse.
+ * Normally used in variations of MOD 11 algorithmic checks.
+ */
+export function reverseMultiplyAndSum(digits, base) {
+ let total = 0;
+ for (let i = 0; i < digits.length; i++) {
+ total += digits[i] * (base - i);
+ }
+ return total;
+}
+
+/*
+ * Verhoeff validation helper function
+ * Called with a string of numbers
+ * to validate according to the Verhoeff algorithm.
+ */
+export function verhoeffCheck(str) {
+ const d_table = [
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
+ [1, 2, 3, 4, 0, 6, 7, 8, 9, 5],
+ [2, 3, 4, 0, 1, 7, 8, 9, 5, 6],
+ [3, 4, 0, 1, 2, 8, 9, 5, 6, 7],
+ [4, 0, 1, 2, 3, 9, 5, 6, 7, 8],
+ [5, 9, 8, 7, 6, 0, 4, 3, 2, 1],
+ [6, 5, 9, 8, 7, 1, 0, 4, 3, 2],
+ [7, 6, 5, 9, 8, 2, 1, 0, 4, 3],
+ [8, 7, 6, 5, 9, 3, 2, 1, 0, 4],
+ [9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
+ ];
+
+ const p_table = [
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
+ [1, 5, 7, 6, 2, 8, 3, 0, 9, 4],
+ [5, 8, 0, 3, 7, 9, 6, 1, 4, 2],
+ [8, 9, 1, 6, 0, 4, 3, 5, 2, 7],
+ [9, 4, 5, 3, 1, 2, 6, 8, 7, 0],
+ [4, 2, 8, 6, 5, 7, 3, 9, 0, 1],
+ [2, 7, 9, 3, 8, 0, 6, 4, 1, 5],
+ [7, 0, 4, 6, 9, 1, 3, 2, 5, 8],
+ ];
+
+ // Copy (to prevent replacement) and reverse
+ const str_copy = str.split('').reverse().join('');
+ let checksum = 0;
+ for (let i = 0; i < str_copy.length; i++) {
+ checksum = d_table[checksum][p_table[i % 8][parseInt(str_copy[i], 10)]];
+ }
+ return checksum === 0;
+}
diff --git a/src/lib/util/assertString.js b/src/lib/util/assertString.js
index e4fac8854..3baa4452f 100644
--- a/src/lib/util/assertString.js
+++ b/src/lib/util/assertString.js
@@ -1,7 +1,4 @@
export default function assertString(input) {
- const isString = (typeof input === 'string' || input instanceof String);
-
- if (!isString) {
- throw new TypeError('This library (validator.js) validates strings only');
- }
+ if (input === undefined || input === null) throw new TypeError(`Expected a string but received a ${input}`);
+ if (input.constructor.name !== 'String') throw new TypeError(`Expected a string but received a ${input.constructor.name}`);
}
diff --git a/src/lib/util/checkHost.js b/src/lib/util/checkHost.js
new file mode 100644
index 000000000..ed1dddefe
--- /dev/null
+++ b/src/lib/util/checkHost.js
@@ -0,0 +1,13 @@
+function isRegExp(obj) {
+ return Object.prototype.toString.call(obj) === '[object RegExp]';
+}
+
+export default function checkHost(host, matches) {
+ for (let i = 0; i < matches.length; i++) {
+ let match = matches[i];
+ if (host === match || (isRegExp(match) && match.test(host))) {
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/src/lib/util/includesArray.js b/src/lib/util/includesArray.js
new file mode 100644
index 000000000..70ace8857
--- /dev/null
+++ b/src/lib/util/includesArray.js
@@ -0,0 +1,3 @@
+const includes = (arr, val) => arr.some(arrVal => val === arrVal);
+
+export default includes;
diff --git a/src/lib/util/includesString.js b/src/lib/util/includesString.js
new file mode 100644
index 000000000..41a50bba8
--- /dev/null
+++ b/src/lib/util/includesString.js
@@ -0,0 +1,3 @@
+const includes = (str, val) => str.indexOf(val) !== -1;
+
+export default includes;
diff --git a/src/lib/util/multilineRegex.js b/src/lib/util/multilineRegex.js
new file mode 100644
index 000000000..706d8a639
--- /dev/null
+++ b/src/lib/util/multilineRegex.js
@@ -0,0 +1,13 @@
+/**
+ * Build RegExp object from an array
+ * of multiple/multi-line regexp parts
+ *
+ * @param {string[]} parts
+ * @param {string} flags
+ * @return {object} - RegExp object
+ */
+export default function multilineRegexp(parts, flags) {
+ const regexpAsStringLiteral = parts.join('');
+
+ return new RegExp(regexpAsStringLiteral, flags);
+}
diff --git a/src/lib/util/nullUndefinedCheck.js b/src/lib/util/nullUndefinedCheck.js
new file mode 100644
index 000000000..c45bfbe82
--- /dev/null
+++ b/src/lib/util/nullUndefinedCheck.js
@@ -0,0 +1,3 @@
+export default function isNullOrUndefined(value) {
+ return value === null || value === undefined;
+}
diff --git a/src/lib/util/typeOf.js b/src/lib/util/typeOf.js
new file mode 100644
index 000000000..f96af511a
--- /dev/null
+++ b/src/lib/util/typeOf.js
@@ -0,0 +1,10 @@
+/**
+ * Better way to handle type checking
+ * null, {}, array and date are objects, which confuses
+ */
+export default function typeOf(input) {
+ const rawObject = Object.prototype.toString.call(input).toLowerCase();
+ const typeOfRegex = /\[object (.*)]/g;
+ const type = typeOfRegex.exec(rawObject)[1];
+ return type;
+}
diff --git a/test/.eslintrc.json b/test/.eslintrc.json
deleted file mode 100644
index 4b03fb38c..000000000
--- a/test/.eslintrc.json
+++ /dev/null
@@ -1,15 +0,0 @@
-{
- "parserOptions": {
- "ecmaVersion": 5,
- "sourceType": "module"
- },
- "rules": {
- "no-var": 0,
- "vars-on-top": 0,
- "prefer-arrow-callback": 0,
- "object-shorthand": [0],
- "quote-props": 0,
- "no-plusplus": 0,
- "no-bitwise": 0
- }
-}
diff --git a/test/client-side.js b/test/client-side.js
deleted file mode 100644
index 503ca2b2f..000000000
--- a/test/client-side.js
+++ /dev/null
@@ -1,27 +0,0 @@
-var assert = require('assert');
-var validator = require('../validator');
-var min = require('../validator.min');
-
-describe('Minified version', function () {
- it('should export the same things as the server-side version', function () {
- for (var key in validator) {
- if ({}.hasOwnProperty.call(validator, key)) {
- assert.equal(typeof validator[key],
- typeof min[key], `Minified version did not export ${key}`);
- }
- }
- });
-
- it('should be up to date', function () {
- assert.equal(min.version, validator.version, 'Minified version mismatch. Run `make min`');
- });
-
- it('should validate strings', function () {
- assert.equal(min.isEmail('foo@bar.com'), true);
- assert.equal(min.isEmail('foo'), false);
- });
-
- it('should sanitize strings', function () {
- assert.equal(min.toBoolean('1'), true);
- });
-});
diff --git a/test/clientSide.test.js b/test/clientSide.test.js
new file mode 100644
index 000000000..f0b5ca72c
--- /dev/null
+++ b/test/clientSide.test.js
@@ -0,0 +1,29 @@
+import assert from 'assert';
+import validator from '../validator';
+import min from '../validator.min';
+
+describe('Minified version', () => {
+ it('should export the same things as the server-side version', () => {
+ for (let key in validator) {
+ if ({}.hasOwnProperty.call(validator, key)) {
+ assert.strictEqual(
+ typeof validator[key],
+ typeof min[key], `Minified version did not export ${key}`
+ );
+ }
+ }
+ });
+
+ it('should be up to date', () => {
+ assert.strictEqual(min.version, validator.version, 'Minified version mismatch. Run `make min`');
+ });
+
+ it('should validate strings', () => {
+ assert.strictEqual(min.isEmail('foo@bar.com'), true);
+ assert.strictEqual(min.isEmail('foo'), false);
+ });
+
+ it('should sanitize strings', () => {
+ assert.strictEqual(min.toBoolean('1'), true);
+ });
+});
diff --git a/test/exports.js b/test/exports.js
deleted file mode 100644
index ec9c5961e..000000000
--- a/test/exports.js
+++ /dev/null
@@ -1,26 +0,0 @@
-var assert = require('assert');
-var validator = require('../index');
-var isPostalCodeLocales = require('../lib/isPostalCode').locales;
-
-describe('Exports', function () {
- it('should export validators', function () {
- assert.equal(typeof validator.isEmail, 'function');
- assert.equal(typeof validator.isAlpha, 'function');
- });
-
- it('should export sanitizers', function () {
- assert.equal(typeof validator.toBoolean, 'function');
- assert.equal(typeof validator.toFloat, 'function');
- });
-
- it('should export the version number', function () {
- /* eslint-disable global-require */
- assert.equal(validator.version, require('../package.json').version,
- 'Version number mismatch in "package.json" vs. "validator.js"');
- /* eslint-enable global-require */
- });
-
- it('should export isPostalCode\'s supported locales', function () {
- assert.ok(isPostalCodeLocales instanceof Array);
- });
-});
diff --git a/test/exports.test.js b/test/exports.test.js
new file mode 100644
index 000000000..a5f458f05
--- /dev/null
+++ b/test/exports.test.js
@@ -0,0 +1,65 @@
+import assert from 'assert';
+import validator from '../index';
+import { locales as isPostalCodeLocales } from '../src/lib/isPostalCode';
+import { locales as isAlphaLocales } from '../src/lib/isAlpha';
+import { locales as isAlphanumericLocales } from '../src/lib/isAlphanumeric';
+import { locales as isMobilePhoneLocales } from '../src/lib/isMobilePhone';
+import { locales as isFloatLocales } from '../src/lib/isFloat';
+import { locales as ibanCountryCodes } from '../src/lib/isIBAN';
+import { locales as passportNumberLocales } from '../src/lib/isPassportNumber';
+
+describe('Exports', () => {
+ it('should export isPassportNumbers\'s supported locales', () => {
+ assert.ok(passportNumberLocales instanceof Array);
+ assert.ok(validator.passportNumberLocales instanceof Array);
+ });
+
+ it('should export validators', () => {
+ assert.strictEqual(typeof validator.isEmail, 'function');
+ assert.strictEqual(typeof validator.isAlpha, 'function');
+ });
+
+ it('should export sanitizers', () => {
+ assert.strictEqual(typeof validator.toBoolean, 'function');
+ assert.strictEqual(typeof validator.toFloat, 'function');
+ });
+
+ it('should export the version number', () => {
+ /* eslint-disable global-require */
+ assert.strictEqual(
+ validator.version, require('../package.json').version,
+ 'Version number mismatch in "package.json" vs. "validator.js"'
+ );
+ /* eslint-enable global-require */
+ });
+
+ it('should export isPostalCode\'s supported locales', () => {
+ assert.ok(isPostalCodeLocales instanceof Array);
+ assert.ok(validator.isPostalCodeLocales instanceof Array);
+ });
+
+ it('should export isAlpha\'s supported locales', () => {
+ assert.ok(isAlphaLocales instanceof Array);
+ assert.ok(validator.isAlphaLocales instanceof Array);
+ });
+
+ it('should export isAlphanumeric\'s supported locales', () => {
+ assert.ok(isAlphanumericLocales instanceof Array);
+ assert.ok(validator.isAlphanumericLocales instanceof Array);
+ });
+
+ it('should export isMobilePhone\'s supported locales', () => {
+ assert.ok(isMobilePhoneLocales instanceof Array);
+ assert.ok(validator.isMobilePhoneLocales instanceof Array);
+ });
+
+ it('should export isFloat\'s supported locales', () => {
+ assert.ok(isFloatLocales instanceof Array);
+ assert.ok(validator.isFloatLocales instanceof Array);
+ });
+
+ it('should export a list of country codes that implement IBAN', () => {
+ assert.ok(ibanCountryCodes instanceof Array);
+ assert.ok(validator.ibanLocales instanceof Array);
+ });
+});
diff --git a/test/sanitizers.js b/test/sanitizers.test.js
similarity index 70%
rename from test/sanitizers.js
rename to test/sanitizers.test.js
index 3fa3120b7..e36ba48d3 100644
--- a/test/sanitizers.js
+++ b/test/sanitizers.test.js
@@ -1,56 +1,68 @@
-var validator = require('../index');
-var format = require('util').format;
+import { format } from 'util';
+import validator from '../src/index';
function test(options) {
- var args = options.args || [];
+ let args = options.args || [];
args.unshift(null);
- Object.keys(options.expect).forEach(function (input) {
+ Object.keys(options.expect).forEach((input) => {
args[0] = input;
- var result = validator[options.sanitizer](...args);
- var expected = options.expect[input];
+ let result = validator[options.sanitizer](...args);
+ let expected = options.expect[input];
if (isNaN(result) && !result.length && isNaN(expected)) {
return;
}
if (result !== expected) {
- var warning = format('validator.%s(%s) returned "%s" but should have returned "%s"',
- options.sanitizer, args.join(', '), result, expected);
+ let warning = format(
+ 'validator.%s(%s) returned "%s" but should have returned "%s"',
+ options.sanitizer, args.join(', '), result, expected
+ );
throw new Error(warning);
}
});
}
-describe('Sanitizers', function () {
- it('should sanitize boolean strings', function () {
+describe('Sanitizers', () => {
+ it('should sanitize boolean strings', () => {
test({
sanitizer: 'toBoolean',
expect: {
- '0': false,
+ 0: false,
'': false,
- '1': true,
- 'true': true,
- 'foobar': true,
+ 1: true,
+ true: true,
+ True: true,
+ TRUE: true,
+ foobar: true,
' ': true,
+ false: false,
+ False: false,
+ FALSE: false,
},
});
test({
sanitizer: 'toBoolean',
args: [true], // strict
expect: {
- '0': false,
+ 0: false,
'': false,
- '1': true,
- 'true': true,
- 'foobar': false,
+ 1: true,
+ true: true,
+ True: true,
+ TRUE: true,
+ foobar: false,
' ': false,
+ false: false,
+ False: false,
+ FALSE: false,
},
});
});
- it('should trim whitespace', function () {
+ it('should trim whitespace', () => {
test({
sanitizer: 'trim',
expect: {
@@ -76,7 +88,7 @@ describe('Sanitizers', function () {
});
});
- it('should trim custom characters', function () {
+ it('should trim custom characters', () => {
test({
sanitizer: 'trim',
args: ['01'],
@@ -89,45 +101,59 @@ describe('Sanitizers', function () {
expect: { '010100201000': '201000' },
});
+ test({
+ sanitizer: 'ltrim',
+ args: ['\\S'],
+ expect: { '\\S01010020100001': '01010020100001' },
+ });
+
+
test({
sanitizer: 'rtrim',
args: ['01'],
expect: { '010100201000': '0101002' },
});
+
+ test({
+ sanitizer: 'rtrim',
+ args: ['\\S'],
+ expect: { '01010020100001\\S': '01010020100001' },
+ });
});
- it('should convert strings to integers', function () {
+ it('should convert strings to integers', () => {
test({
sanitizer: 'toInt',
expect: {
- '3': 3,
+ 3: 3,
' 3 ': 3,
- '2.4': 2,
- 'foo': NaN,
+ 2.4: 2,
+ foo: NaN,
},
});
test({
sanitizer: 'toInt',
args: [16],
- expect: { 'ff': 255 },
+ expect: { ff: 255 },
});
});
- it('should convert strings to floats', function () {
+ it('should convert strings to floats', () => {
test({
sanitizer: 'toFloat',
expect: {
- '2': 2.0,
+ 2: 2.0,
'2.': 2.0,
'-2.5': -2.5,
'.5': 0.5,
- 'foo': NaN,
+ '2020-01-06T14:31:00.135Z': NaN,
+ foo: NaN,
},
});
});
- it('should escape HTML', function () {
+ it('should escape HTML', () => {
test({
sanitizer: 'escape',
expect: {
@@ -146,7 +172,7 @@ describe('Sanitizers', function () {
});
});
- it('should unescape HTML', function () {
+ it('should unescape HTML', () => {
test({
sanitizer: 'unescape',
expect: {
@@ -158,11 +184,14 @@ describe('Sanitizers', function () {
'Backtick: `':
'Backtick: `',
+
+ 'Escaped string: <':
+ 'Escaped string: <',
},
});
});
- it('should remove control characters (<32 and 127)', function () {
+ it('should remove control characters (<32 and 127)', () => {
// Check basic functionality
test({
sanitizer: 'stripLow',
@@ -177,7 +206,7 @@ describe('Sanitizers', function () {
test({
sanitizer: 'stripLow',
expect: {
- 'perch\u00e9': 'perch\u00e9',
+ perché: 'perch\u00e9',
'\u20ac': '\u20ac',
'\u2206\x0A': '\u2206',
'\ud83d\ude04': '\ud83d\ude04',
@@ -194,33 +223,69 @@ describe('Sanitizers', function () {
});
});
- it('should sanitize a string based on a whitelist', function () {
+ it('should sanitize a string based on a whitelist', () => {
test({
sanitizer: 'whitelist',
args: ['abc'],
expect: {
- 'abcdef': 'abc',
- 'aaaaaaaaaabbbbbbbbbb': 'aaaaaaaaaabbbbbbbbbb',
- 'a1b2c3': 'abc',
+ abcdef: 'abc',
+ aaaaaaaaaabbbbbbbbbb: 'aaaaaaaaaabbbbbbbbbb',
+ a1b2c3: 'abc',
' ': '',
},
});
});
- it('should sanitize a string based on a blacklist', function () {
+ it('should sanitize a string based on a blacklist', () => {
test({
sanitizer: 'blacklist',
args: ['abc'],
expect: {
- 'abcdef': 'def',
- 'aaaaaaaaaabbbbbbbbbb': '',
- 'a1b2c3': '123',
+ abcdef: 'def',
+ aaaaaaaaaabbbbbbbbbb: '',
+ a1b2c3: '123',
' ': ' ',
},
});
});
- it('should normalize an email based on domain', function () {
+ it('should score passwords', () => {
+ test({
+ sanitizer: 'isStrongPassword',
+ args: [{
+ returnScore: true,
+ pointsPerUnique: 1,
+ pointsPerRepeat: 0.5,
+ pointsForContainingLower: 10,
+ pointsForContainingUpper: 10,
+ pointsForContainingNumber: 10,
+ pointsForContainingSymbol: 10,
+ }],
+ expect: {
+ abc: 13,
+ abcc: 13.5,
+ aBc: 23,
+ 'Abc123!': 47,
+ '!@#$%^&*()': 20,
+ },
+ });
+ });
+
+ it('should score passwords with default options', () => {
+ test({
+ sanitizer: 'isStrongPassword',
+ expect: {
+ abc: false,
+ abcc: false,
+ aBc: false,
+ 'Abc123!': false,
+ '!@#$%^&*()': false,
+ 'abc123!@f#rA': true,
+ },
+ });
+ });
+
+ it('should normalize an email based on domain', () => {
test({
sanitizer: 'normalizeEmail',
expect: {
@@ -235,8 +300,19 @@ describe('Sanitizers', function () {
'some.name.midd.leNa.me.+extension@GoogleMail.com': 'somenamemiddlename@gmail.com',
'some.name+extension@unknown.com': 'some.name+extension@unknown.com',
'hans@m端ller.com': 'hans@m端ller.com',
- 'some.name.midd..leNa...me...+extension@GoogleMail.com': 'somenamemiddlename@gmail.com',
+ 'some.name.midd..leNa...me...+extension@GoogleMail.com': 'somenamemidd..lena...me...@gmail.com',
+ 'matthew..example@gmail.com': 'matthew..example@gmail.com',
'"foo@bar"@baz.com': '"foo@bar"@baz.com',
+ 'test@ya.ru': 'test@yandex.ru',
+ 'test@yandex.kz': 'test@yandex.ru',
+ 'test@yandex.ru': 'test@yandex.ru',
+ 'test@yandex.ua': 'test@yandex.ru',
+ 'test@yandex.com': 'test@yandex.ru',
+ 'test@yandex.by': 'test@yandex.ru',
+ '@gmail.com': false,
+ '@icloud.com': false,
+ '@outlook.com': false,
+ '@yahoo.com': false,
},
});
@@ -259,6 +335,7 @@ describe('Sanitizers', function () {
'SOME.name@yahoo.ca': 'some.name@yahoo.ca',
'SOME.name@outlook.ie': 'some.name@outlook.ie',
'SOME.name@me.com': 'some.name@me.com',
+ 'SOME.name@yandex.ru': 'some.name@yandex.ru',
},
});
@@ -271,6 +348,7 @@ describe('Sanitizers', function () {
icloud_lowercase: false,
outlookdotcom_lowercase: false,
yahoo_lowercase: false,
+ yandex_lowercase: false,
}],
expect: {
'TEST@FOO.COM': 'TEST@foo.com', // all_lowercase
@@ -280,6 +358,7 @@ describe('Sanitizers', function () {
'ME@outlook.COM': 'ME@outlook.com', // outlookdotcom_lowercase
'JOHN@live.CA': 'JOHN@live.ca', // outlookdotcom_lowercase
'ME@ymail.COM': 'ME@ymail.com', // yahoo_lowercase
+ 'ME@yandex.RU': 'ME@yandex.ru', // yandex_lowercase
},
});
@@ -326,6 +405,7 @@ describe('Sanitizers', function () {
expect: {
'SOME.name@GMAIL.com': 'somename@gmail.com',
'SOME.name+me@GMAIL.com': 'somename@gmail.com',
+ 'some.name..multiple@gmail.com': 'somename..multiple@gmail.com',
'my.self@foo.com': 'my.self@foo.com',
},
});
@@ -401,5 +481,34 @@ describe('Sanitizers', function () {
'my.self@foo.com': 'my.self@foo.com',
},
});
+
+ // Testing yandex_convert_yandexru
+ test({
+ sanitizer: 'normalizeEmail',
+ args: [{
+ yandex_convert_yandexru: false,
+ }],
+ expect: {
+ 'test@yandex.kz': 'test@yandex.kz',
+ 'test@yandex.ru': 'test@yandex.ru',
+ 'test@yandex.ua': 'test@yandex.ua',
+ 'test@yandex.com': 'test@yandex.com',
+ 'test@yandex.by': 'test@yandex.by',
+ },
+ });
+
+ test({
+ sanitizer: 'normalizeEmail',
+ args: [{
+ yandex_convert_yandexru: true,
+ }],
+ expect: {
+ 'test@yandex.kz': 'test@yandex.ru',
+ 'test@yandex.ru': 'test@yandex.ru',
+ 'test@yandex.ua': 'test@yandex.ru',
+ 'test@yandex.com': 'test@yandex.ru',
+ 'test@yandex.by': 'test@yandex.ru',
+ },
+ });
});
});
diff --git a/test/testFunctions.js b/test/testFunctions.js
new file mode 100644
index 000000000..5fc133bec
--- /dev/null
+++ b/test/testFunctions.js
@@ -0,0 +1,60 @@
+import assert from 'assert';
+import { format } from 'util';
+import validator from '../src/index';
+
+function stringifyArgs(argsArr) {
+ return argsArr.map(arg => JSON.stringify(arg)).join(', ');
+}
+
+export default function test(options) {
+ const args = options.args || [];
+
+ args.unshift(null);
+
+ if (options.error) {
+ options.error.forEach((error) => {
+ args[0] = error;
+
+ try {
+ assert.throws(() => validator[options.validator](...args));
+ } catch (err) {
+ const warning = format(
+ 'validator.%s(%s) passed but should error',
+ options.validator, stringifyArgs(args)
+ );
+
+ throw new Error(warning);
+ }
+ });
+ }
+
+ if (options.valid) {
+ options.valid.forEach((valid) => {
+ args[0] = valid;
+
+ if (validator[options.validator](...args) !== true) {
+ const warning = format(
+ 'validator.%s(%s) failed but should have passed',
+ options.validator, stringifyArgs(args)
+ );
+
+ throw new Error(warning);
+ }
+ });
+ }
+
+ if (options.invalid) {
+ options.invalid.forEach((invalid) => {
+ args[0] = invalid;
+
+ if (validator[options.validator](...args) !== false) {
+ const warning = format(
+ 'validator.%s(%s) passed but should have failed',
+ options.validator, stringifyArgs(args)
+ );
+
+ throw new Error(warning);
+ }
+ });
+ }
+}
diff --git a/test/util.test.js b/test/util.test.js
new file mode 100644
index 000000000..0146a4e2c
--- /dev/null
+++ b/test/util.test.js
@@ -0,0 +1,64 @@
+/**
+ * All tests that tests any utility.
+ * Prevent any breaking of functionality
+ */
+import assert from 'assert';
+import typeOf from '../src/lib/util/typeOf';
+import assertString from '../src/lib/util/assertString';
+
+
+describe('Util', () => {
+ it('should validate different typeOf', () => {
+ assert.strictEqual(typeOf([]), 'array');
+ assert.strictEqual(typeOf(null), 'null');
+ assert.strictEqual(typeOf({}), 'object');
+ assert.strictEqual(typeOf(new Date()), 'date');
+ assert.strictEqual(typeOf('ezkemboi'), 'string');
+ assert.strictEqual(typeOf(String('kemboi')), 'string');
+ assert.strictEqual(typeOf(undefined), 'undefined');
+ assert.strictEqual(typeOf(2021), 'number');
+ assert.notStrictEqual(typeOf([]), 'object');
+ });
+});
+
+describe('assertString', () => {
+ it('Should throw an error if argument provided is an undefined', () => {
+ assert.throws(() => { assertString(); }, TypeError);
+ });
+
+ it('Should throw an error if argument provided is a null', () => {
+ assert.throws(() => { assertString(null); }, TypeError);
+ });
+
+ it('Should throw an error if argument provided is a Boolean', () => {
+ assert.throws(() => { assertString(true); }, TypeError);
+ });
+
+ it('Should throw an error if argument provided is a Date', () => {
+ assert.throws(() => { assertString(new Date()); }, TypeError);
+ });
+
+ it('Should throw an error if argument provided is a Number(NaN)', () => {
+ assert.throws(() => { assertString(NaN); }, TypeError);
+ });
+
+ it('Should throw an error if argument provided is a Number', () => {
+ assert.throws(() => { assertString(2024); }, TypeError);
+ });
+
+ it('Should throw an error if argument provided is an Object', () => {
+ assert.throws(() => { assertString({}); }, TypeError);
+ });
+
+ it('Should throw an error if argument provided is an Array', () => {
+ assert.throws(() => { assertString([]); }, TypeError);
+ });
+
+ it('Should not throw an error if the argument is an empty string', () => {
+ assert.doesNotThrow(() => { assertString(''); });
+ });
+
+ it('Should not throw an error if the argument is a String', () => {
+ assert.doesNotThrow(() => { assertString('antidisestablishmentarianism'); });
+ });
+});
diff --git a/test/validators.js b/test/validators.js
deleted file mode 100644
index 235299fc3..000000000
--- a/test/validators.js
+++ /dev/null
@@ -1,5144 +0,0 @@
-var validator = require('../index'),
- format = require('util').format,
- assert = require('assert'),
- path = require('path'),
- fs = require('fs'),
- vm = require('vm');
-
-var validator_js = fs.readFileSync(path.join(__dirname, '../validator.js')).toString();
-
-function test(options) {
- var args = options.args || [];
- args.unshift(null);
- if (options.valid) {
- options.valid.forEach(function (valid) {
- args[0] = valid;
- if (validator[options.validator](...args) !== true) {
- var warning = format('validator.%s(%s) failed but should have passed',
- options.validator, args.join(', '));
- throw new Error(warning);
- }
- });
- }
- if (options.invalid) {
- options.invalid.forEach(function (invalid) {
- args[0] = invalid;
- if (validator[options.validator](...args) !== false) {
- var warning = format('validator.%s(%s) passed but should have failed',
- options.validator, args.join(', '));
- throw new Error(warning);
- }
- });
- }
-}
-
-function repeat(str, count) {
- var result = '';
- while (count--) {
- result += str;
- }
- return result;
-}
-
-describe('Validators', function () {
- it('should validate email addresses', function () {
- test({
- validator: 'isEmail',
- valid: [
- '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',
- 'test+ext@gmail.com',
- 'some.name.midd.leNa.me.+extension@GoogleMail.com',
- 'gmail...ignores...dots...@gmail.com',
- '"foobar"@example.com',
- '" foo m端ller "@example.com',
- '"foo\\@bar"@example.com',
- `${repeat('a', 64)}@${repeat('a', 250)}.com`,
- ],
- invalid: [
- 'invalidemail@',
- 'invalid.com',
- '@invalid.com',
- 'foo@bar.com.',
- 'somename@gmail.com',
- 'foo@bar.co.uk.',
- 'z@co.c',
- 'gmailgmailgmailgmailgmail@gmail.com',
- `${repeat('a', 64)}@${repeat('a', 251)}.com`,
- `${repeat('a', 65)}@${repeat('a', 250)}.com`,
- 'test1@invalid.co m',
- 'test2@invalid.co m',
- 'test3@invalid.co m',
- 'test4@invalid.co m',
- 'test5@invalid.co m',
- 'test6@invalid.co m',
- 'test7@invalid.co m',
- 'test8@invalid.co m',
- 'test9@invalid.co m',
- 'test10@invalid.co m',
- 'test11@invalid.co m',
- 'test12@invalid.co m',
- 'test13@invalid.co m',
- ],
- });
- });
-
- it('should validate email addresses without UTF8 characters in local part', function () {
- test({
- validator: 'isEmail',
- args: [{ allow_utf8_local_part: false }],
- valid: [
- 'foo@bar.com',
- 'x@x.au',
- 'foo@bar.com.au',
- 'foo+bar@bar.com',
- 'hans@m端ller.com',
- 'test|123@m端ller.com',
- 'test+ext@gmail.com',
- 'some.name.midd.leNa.me.+extension@GoogleMail.com',
- '"foobar"@example.com',
- '"foo\\@bar"@example.com',
- '" foo bar "@example.com',
- ],
- invalid: [
- 'invalidemail@',
- 'invalid.com',
- '@invalid.com',
- 'foo@bar.com.',
- 'foo@bar.co.uk.',
- 'somename@gmail.com',
- 'hans.m端ller@test.com',
- 'z@co.c',
- 'tüst@invalid.com',
- ],
- });
- });
-
- it('should validate email addresses with display names', function () {
- test({
- validator: 'isEmail',
- args: [{ allow_display_name: true }],
- valid: [
- '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',
- 'test+ext@gmail.com',
- 'some.name.midd.leNa.me.+extension@GoogleMail.com',
- 'Some Name ',
- 'Some Name ',
- 'Some Name ',
- 'Some Name ',
- 'Some Name ',
- 'Some Name ',
- 'Some Name ',
- 'Some Name ',
- '\'Foo Bar, Esq\'',
- 'Some Name ',
- 'Some Middle Name ',
- 'Name ',
- 'Name',
- ],
- invalid: [
- 'invalidemail@',
- 'invalid.com',
- '@invalid.com',
- 'foo@bar.com.',
- 'foo@bar.co.uk.',
- 'Some Name ',
- 'Some Name ',
- 'Some Name <@invalid.com>',
- 'Some Name ',
- 'Some Name ',
- 'Some Name foo@bar.co.uk.>',
- 'Some Name ',
- 'Name foo@bar.co.uk',
- ],
- });
- });
-
- it('should validate email addresses with required display names', function () {
- test({
- validator: 'isEmail',
- args: [{ require_display_name: true }],
- valid: [
- 'Some Name ',
- 'Some Name ',
- 'Some Name ',
- 'Some Name ',
- 'Some Name ',
- 'Some Name ',
- 'Some Name ',
- 'Some Name ',
- 'Some Name ',
- 'Some Middle Name ',
- 'Name ',
- 'Name',
- ],
- invalid: [
- 'some.name.midd.leNa.me.+extension@GoogleMail.com',
- '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',
- 'test+ext@gmail.com',
- 'invalidemail@',
- 'invalid.com',
- '@invalid.com',
- 'foo@bar.com.',
- 'foo@bar.co.uk.',
- 'Some Name ',
- 'Some Name ',
- 'Some Name <@invalid.com>',
- 'Some Name ',
- 'Some Name ',
- 'Some Name foo@bar.co.uk.>',
- 'Some Name ',
- 'Name foo@bar.co.uk',
- ],
- });
- });
-
-
- it('should validate URLs', function () {
- test({
- validator: 'isURL',
- valid: [
- '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',
- 'http://xn------eddceddeftq7bvv7c4ke4c.xn--p1ai',
- 'http://кулік.укр',
- 'test.com?ref=http://test2.com',
- 'http://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:80/index.html',
- 'http://[1080:0:0:0:8:800:200C:417A]/index.html',
- 'http://[3ffe:2a00:100:7031::1]',
- 'http://[1080::8:800:200C:417A]/foo',
- 'http://[::192.9.5.5]/ipng',
- 'http://[::FFFF:129.144.52.38]:80/index.html',
- 'http://[2010:836B:4179::836B:4179]',
- ],
- invalid: [
- 'http://localhost:3000/',
- '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://foobar.com/${new Array(2083).join('f')}`,
- 'http://*.foo.com',
- '*.foo.com',
- '!.foo.com',
- 'http://example.com.',
- 'http://localhost:61500this is an invalid url!!!!',
- '////foobar.com',
- 'http:////foobar.com',
- 'https://example.com/foo//',
- ],
- });
- });
-
- it('should validate URLs with custom protocols', function () {
- test({
- validator: 'isURL',
- args: [{
- protocols: ['rtmp'],
- }],
- valid: [
- 'rtmp://foobar.com',
- ],
- invalid: [
- 'http://foobar.com',
- ],
- });
- });
-
- it('should validate file URLs without a host', function () {
- test({
- validator: 'isURL',
- args: [{
- protocols: ['file'],
- require_host: false,
- require_tld: false,
- }],
- valid: [
- 'file://localhost/foo.txt',
- 'file:///foo.txt',
- 'file:///',
- ],
- invalid: [
- 'http://foobar.com',
- 'file://',
- ],
- });
- });
-
- it('should validate URLs with any protocol', function () {
- test({
- validator: 'isURL',
- args: [{
- require_valid_protocol: false,
- }],
- valid: [
- 'rtmp://foobar.com',
- 'http://foobar.com',
- 'test://foobar.com',
- ],
- invalid: [
- 'mailto:test@example.com',
- ],
- });
- });
-
- it('should validate URLs with underscores', function () {
- test({
- validator: 'isURL',
- args: [{
- allow_underscores: true,
- }],
- valid: [
- 'http://foo_bar.com',
- 'http://pr.example_com.294.example.com/',
- 'http://foo__bar.com',
- ],
- invalid: [],
- });
- });
-
- it('should validate URLs that do not have a TLD', function () {
- test({
- validator: 'isURL',
- args: [{
- require_tld: false,
- }],
- valid: [
- 'http://foobar.com/',
- 'http://foobar/',
- 'http://localhost/',
- 'foobar/',
- 'foobar',
- ],
- invalid: [],
- });
- });
-
- it('should validate URLs with a trailing dot option', function () {
- test({
- validator: 'isURL',
- args: [{
- allow_trailing_dot: true,
- require_tld: false,
- }],
- valid: [
- 'http://example.com.',
- 'foobar.',
- ],
- });
- });
-
- it('should validate protocol relative URLs', function () {
- test({
- validator: 'isURL',
- args: [{
- allow_protocol_relative_urls: true,
- }],
- valid: [
- '//foobar.com',
- 'http://foobar.com',
- 'foobar.com',
- ],
- invalid: [
- '://foobar.com',
- '/foobar.com',
- '////foobar.com',
- 'http:////foobar.com',
- ],
- });
- });
-
- it('should not validate protocol relative URLs when require protocol is true', function () {
- test({
- validator: 'isURL',
- args: [{
- allow_protocol_relative_urls: true,
- require_protocol: true,
- }],
- valid: [
- 'http://foobar.com',
- ],
- invalid: [
- '//foobar.com',
- '://foobar.com',
- '/foobar.com',
- 'foobar.com',
- ],
- });
- });
-
- it('should let users specify whether URLs require a protocol', function () {
- test({
- validator: 'isURL',
- args: [{
- require_protocol: true,
- }],
- valid: [
- 'http://foobar.com/',
- ],
- invalid: [
- 'http://localhost/',
- 'foobar.com',
- 'foobar',
- ],
- });
- });
-
- it('should let users specify a host whitelist', function () {
- test({
- validator: 'isURL',
- args: [{
- host_whitelist: ['foo.com', 'bar.com'],
- }],
- valid: [
- 'http://bar.com/',
- 'http://foo.com/',
- ],
- invalid: [
- 'http://foobar.com',
- 'http://foo.bar.com/',
- 'http://qux.com',
- ],
- });
- });
-
- it('should allow regular expressions in the host whitelist', function () {
- test({
- validator: 'isURL',
- args: [{
- host_whitelist: ['bar.com', 'foo.com', /\.foo\.com$/],
- }],
- valid: [
- 'http://bar.com/',
- 'http://foo.com/',
- 'http://images.foo.com/',
- 'http://cdn.foo.com/',
- 'http://a.b.c.foo.com/',
- ],
- invalid: [
- 'http://foobar.com',
- 'http://foo.bar.com/',
- 'http://qux.com',
- ],
- });
- });
-
- it('should let users specify a host blacklist', function () {
- test({
- validator: 'isURL',
- args: [{
- host_blacklist: ['foo.com', 'bar.com'],
- }],
- valid: [
- 'http://foobar.com',
- 'http://foo.bar.com/',
- 'http://qux.com',
- ],
- invalid: [
- 'http://bar.com/',
- 'http://foo.com/',
- ],
- });
- });
-
- it('should allow regular expressions in the host blacklist', function () {
- test({
- validator: 'isURL',
- args: [{
- host_blacklist: ['bar.com', 'foo.com', /\.foo\.com$/],
- }],
- valid: [
- 'http://foobar.com',
- 'http://foo.bar.com/',
- 'http://qux.com',
- ],
- invalid: [
- 'http://bar.com/',
- 'http://foo.com/',
- 'http://images.foo.com/',
- 'http://cdn.foo.com/',
- 'http://a.b.c.foo.com/',
- ],
- });
- });
-
- it('should validate MAC addresses', function () {
- test({
- validator: 'isMACAddress',
- valid: [
- 'ab:ab:ab:ab:ab:ab',
- 'FF:FF:FF:FF:FF:FF',
- '01:02:03:04:05:ab',
- '01:AB:03:04:05:06',
- ],
- invalid: [
- 'abc',
- '01:02:03:04:05',
- '01:02:03:04::ab',
- '1:2:3:4:5:6',
- 'AB:CD:EF:GH:01:02',
- ],
- });
- });
-
- it('should validate IP addresses', function () {
- test({
- validator: 'isIP',
- valid: [
- '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',
- ],
- invalid: [
- 'abc',
- '256.0.0.0',
- '0.0.0.256',
- '26.0.0.256',
- '0200.200.200.200',
- '200.0200.200.200',
- '200.200.0200.200',
- '200.200.200.0200',
- '::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',
- ],
- });
- test({
- validator: 'isIP',
- args: [4],
- valid: [
- '127.0.0.1',
- '0.0.0.0',
- '255.255.255.255',
- '1.2.3.4',
- ],
- invalid: [
- '::1',
- '2001:db8:0000:1:1:1:1:1',
- '::ffff:127.0.0.1',
- ],
- });
- test({
- validator: 'isIP',
- args: [6],
- valid: [
- '::1',
- '2001:db8:0000:1:1:1:1:1',
- '::ffff:127.0.0.1',
- ],
- invalid: [
- '127.0.0.1',
- '0.0.0.0',
- '255.255.255.255',
- '1.2.3.4',
- '::ffff:287.0.0.1',
- ],
- });
- test({
- validator: 'isIP',
- args: [10],
- valid: [],
- invalid: [
- '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',
- ],
- });
- });
-
- it('should validate FQDN', function () {
- test({
- validator: 'isFQDN',
- valid: [
- 'domain.com',
- 'dom.plato',
- 'a.domain.co',
- 'foo--bar.com',
- 'xn--froschgrn-x9a.com',
- 'rebecca.blackfriday',
- ],
- invalid: [
- 'abc',
- '256.0.0.0',
- '_.com',
- '*.some.com',
- 's!ome.com',
- 'domain.com/',
- '/more.com',
- ],
- });
- });
- it('should validate FQDN with trailing dot option', function () {
- test({
- validator: 'isFQDN',
- args: [
- { allow_trailing_dot: true },
- ],
- valid: [
- 'example.com.',
- ],
- });
- });
-
- it('should validate alpha strings', function () {
- test({
- validator: 'isAlpha',
- valid: [
- 'abc',
- 'ABC',
- 'FoObar',
- ],
- invalid: [
- 'abc1',
- ' foo ',
- '',
- 'ÄBC',
- 'FÜübar',
- 'Jön',
- 'Heiß',
- ],
- });
- });
-
- it('should validate czech alpha strings', function () {
- test({
- validator: 'isAlpha',
- args: ['cs-CZ'],
- valid: [
- 'žluťoučký',
- 'KŮŇ',
- 'Pěl',
- 'Ďábelské',
- 'ódy',
- ],
- invalid: [
- 'ábc1',
- ' fůj ',
- '',
- ],
- });
- });
-
- it('should validate danish alpha strings', function () {
- test({
- validator: 'isAlpha',
- args: ['da-DK'],
- valid: [
- 'aøå',
- 'Ære',
- 'Øre',
- 'Åre',
- ],
- invalid: [
- 'äbc123',
- 'ÄBC11',
- '',
- ],
- });
- });
-
- it('should validate dutch alpha strings', function () {
- test({
- validator: 'isAlpha',
- args: ['nl-NL'],
- valid: [
- 'Kán',
- 'één',
- 'vóór',
- 'nú',
- 'héél',
- ],
- invalid: [
- 'äca ',
- 'abcß',
- 'Øre',
- ],
- });
- });
-
- it('should validate german alpha strings', function () {
- test({
- validator: 'isAlpha',
- args: ['de-DE'],
- valid: [
- 'äbc',
- 'ÄBC',
- 'FöÖbär',
- 'Heiß',
- ],
- invalid: [
- 'äbc1',
- ' föö ',
- '',
- ],
- });
- });
-
- it('should validate hungarian alpha strings', function () {
- test({
- validator: 'isAlpha',
- args: ['hu-HU'],
- valid: [
- 'árvíztűrőtükörfúrógép',
- 'ÁRVÍZTŰRŐTÜKÖRFÚRÓGÉP',
- ],
- invalid: [
- 'äbc1',
- ' fäö ',
- 'Heiß',
- '',
- ],
- });
- });
-
- it('should validate italian alpha strings', function () {
- test({
- validator: 'isAlpha',
- args: ['it-IT'],
- valid: [
- 'àéèìîóòù',
- 'correnti',
- 'DEFINIZIONE',
- 'compilazione',
- 'metró',
- 'pèsca',
- 'PÉSCA',
- 'genî',
- ],
- invalid: [
- 'äbc123',
- 'ÄBC11',
- 'æøå',
- '',
- ],
- });
- });
-
- it('should validate arabic alpha strings', function () {
- test({
- validator: 'isAlpha',
- args: ['ar'],
- valid: [
- 'أبت',
- 'اَبِتَثّجً',
- ],
- invalid: [
- '١٢٣أبت',
- '١٢٣',
- 'abc1',
- ' foo ',
- '',
- 'ÄBC',
- 'FÜübar',
- 'Jön',
- 'Heiß',
- ],
- });
- });
-
- it('should validate norwegian alpha strings', function () {
- test({
- validator: 'isAlpha',
- args: ['nb-NO'],
- valid: [
- 'aøå',
- 'Ære',
- 'Øre',
- 'Åre',
- ],
- invalid: [
- 'äbc123',
- 'ÄBC11',
- '',
- ],
- });
- });
-
- it('should validate polish alpha strings', function () {
- test({
- validator: 'isAlpha',
- args: ['pl-PL'],
- valid: [
- 'kreską',
- 'zamknięte',
- 'zwykłe',
- 'kropką',
- 'przyjęły',
- 'święty',
- 'Pozwól',
- ],
- invalid: [
- '12řiď ',
- 'blé!!',
- 'föö!2!',
- ],
- });
- });
-
- it('should validate serbian cyrillic alpha strings', function () {
- test({
- validator: 'isAlpha',
- args: ['sr-RS'],
- valid: [
- 'ШћжЂљЕ',
- 'ЧПСТЋЏ',
- ],
- invalid: [
- 'řiď ',
- 'blé33!!',
- 'föö!!',
- ],
- });
- });
-
- it('should validate serbian latin alpha strings', function () {
- test({
- validator: 'isAlpha',
- args: ['sr-RS@latin'],
- valid: [
- 'ŠAabčšđćž',
- 'ŠATROĆčđš',
- ],
- invalid: [
- '12řiď ',
- 'blé!!',
- 'föö!2!',
- ],
- });
- });
-
- it('should validate spanish alpha strings', function () {
- test({
- validator: 'isAlpha',
- args: ['es-ES'],
- valid: [
- 'ábcó',
- 'ÁBCÓ',
- 'dormís',
- 'volvés',
- 'español',
- ],
- invalid: [
- 'äca ',
- 'abcß',
- 'föö!!',
- ],
- });
- });
-
- it('should validate swedish alpha strings', function () {
- test({
- validator: 'isAlpha',
- args: ['sv-SE'],
- valid: [
- 'religiös',
- 'stjäla',
- 'västgöte',
- 'Åre',
- ],
- invalid: [
- 'AİıÖöÇ窺ĞğÜüZ',
- 'religiös23',
- '',
- ],
- });
- });
-
- it('should validate defined arabic locales alpha strings', function () {
- test({
- validator: 'isAlpha',
- args: ['ar-SY'],
- valid: [
- 'أبت',
- 'اَبِتَثّجً',
- ],
- invalid: [
- '١٢٣أبت',
- '١٢٣',
- 'abc1',
- ' foo ',
- '',
- 'ÄBC',
- 'FÜübar',
- 'Jön',
- 'Heiß',
- ],
- });
- });
-
- it('should validate turkish alpha strings', function () {
- test({
- validator: 'isAlpha',
- args: ['tr-TR'],
- valid: [
- 'AİıÖöÇ窺ĞğÜüZ',
- ],
- invalid: [
- '0AİıÖöÇ窺ĞğÜüZ1',
- ' AİıÖöÇ窺ĞğÜüZ ',
- 'abc1',
- ' foo ',
- '',
- 'ÄBC',
- 'Heiß',
- ],
- });
- });
-
- it('should validate urkrainian alpha strings', function () {
- test({
- validator: 'isAlpha',
- args: ['uk-UA'],
- valid: [
- 'АБВГҐДЕЄЖЗИIЇЙКЛМНОПРСТУФХЦШЩЬЮЯ',
- ],
- invalid: [
- '0AİıÖöÇ窺ĞğÜüZ1',
- ' AİıÖöÇ窺ĞğÜüZ ',
- 'abc1',
- ' foo ',
- '',
- 'ÄBC',
- 'Heiß',
- 'ЫыЪъЭэ',
- ],
- });
- });
-
- it('should validate alphanumeric strings', function () {
- test({
- validator: 'isAlphanumeric',
- valid: [
- 'abc123',
- 'ABC11',
- ],
- invalid: [
- 'abc ',
- 'foo!!',
- 'ÄBC',
- 'FÜübar',
- 'Jön',
- ],
- });
- });
-
- it('should validate defined english aliases', function () {
- test({
- validator: 'isAlphanumeric',
- args: ['en-GB'],
- valid: [
- 'abc123',
- 'ABC11',
- ],
- invalid: [
- 'abc ',
- 'foo!!',
- 'ÄBC',
- 'FÜübar',
- 'Jön',
- ],
- });
- });
-
- it('should validate czech alphanumeric strings', function () {
- test({
- validator: 'isAlphanumeric',
- args: ['cs-CZ'],
- valid: [
- 'řiť123',
- 'KŮŇ11',
- ],
- invalid: [
- 'řiď ',
- 'blé!!',
- ],
- });
- });
-
- it('should validate danish alphanumeric strings', function () {
- test({
- validator: 'isAlphanumeric',
- args: ['da-DK'],
- valid: [
- 'ÆØÅ123',
- 'Ære321',
- '321Øre',
- '123Åre',
- ],
- invalid: [
- 'äbc123',
- 'ÄBC11',
- '',
- ],
- });
- });
-
- it('should validate dutch alphanumeric strings', function () {
- test({
- validator: 'isAlphanumeric',
- args: ['nl-NL'],
- valid: [
- 'Kán123',
- 'één354',
- 'v4óór',
- 'nú234',
- 'hé54él',
- ],
- invalid: [
- '1äca ',
- 'ab3cß',
- 'Øre',
- ],
- });
- });
-
- it('should validate german alphanumeric strings', function () {
- test({
- validator: 'isAlphanumeric',
- args: ['de-DE'],
- valid: [
- 'äbc123',
- 'ÄBC11',
- ],
- invalid: [
- 'äca ',
- 'föö!!',
- ],
- });
- });
-
- it('should validate hungarian alphanumeric strings', function () {
- test({
- validator: 'isAlphanumeric',
- args: ['hu-HU'],
- valid: [
- '0árvíztűrőtükörfúrógép123',
- '0ÁRVÍZTŰRŐTÜKÖRFÚRÓGÉP123',
- ],
- invalid: [
- '1időúr!',
- 'äbc1',
- ' fäö ',
- 'Heiß!',
- '',
- ],
- });
- });
-
- it('should validate italian alphanumeric strings', function () {
- test({
- validator: 'isAlphanumeric',
- args: ['it-IT'],
- valid: [
- '123àéèìîóòù',
- '123correnti',
- 'DEFINIZIONE321',
- 'compil123azione',
- 'met23ró',
- 'pès56ca',
- 'PÉS45CA',
- 'gen45î',
- ],
- invalid: [
- 'äbc123',
- 'ÄBC11',
- 'æøå',
- '',
- ],
- });
- });
-
- it('should validate spanish alphanumeric strings', function () {
- test({
- validator: 'isAlphanumeric',
- args: ['es-ES'],
- valid: [
- 'ábcó123',
- 'ÁBCÓ11',
- ],
- invalid: [
- 'äca ',
- 'abcß',
- 'föö!!',
- ],
- });
- });
-
- it('should validate arabic alphanumeric strings', function () {
- test({
- validator: 'isAlphanumeric',
- args: ['ar'],
- valid: [
- 'أبت123',
- 'أبتَُِ١٢٣',
- ],
- invalid: [
- 'äca ',
- 'abcß',
- 'föö!!',
- ],
- });
- });
-
- it('should validate defined arabic aliases', function () {
- test({
- validator: 'isAlphanumeric',
- args: ['ar-SY'],
- valid: [
- 'أبت123',
- 'أبتَُِ١٢٣',
- ],
- invalid: [
- 'abc ',
- 'foo!!',
- 'ÄBC',
- 'FÜübar',
- 'Jön',
- ],
- });
- });
-
- it('should validate norwegian alphanumeric strings', function () {
- test({
- validator: 'isAlphanumeric',
- args: ['nb-NO'],
- valid: [
- 'ÆØÅ123',
- 'Ære321',
- '321Øre',
- '123Åre',
- ],
- invalid: [
- 'äbc123',
- 'ÄBC11',
- '',
- ],
- });
- });
-
- it('should validate polish alphanumeric strings', function () {
- test({
- validator: 'isAlphanumeric',
- args: ['pl-PL'],
- valid: [
- 'kre123ską',
- 'zam21knięte',
- 'zw23ykłe',
- '123',
- 'prz23yjęły',
- 'świ23ęty',
- 'Poz1322wól',
- ],
- invalid: [
- '12řiď ',
- 'blé!!',
- 'föö!2!',
- ],
- });
- });
-
- it('should validate serbian cyrillic alphanumeric strings', function () {
- test({
- validator: 'isAlphanumeric',
- args: ['sr-RS'],
- valid: [
- 'ШћжЂљЕ123',
- 'ЧПСТ132ЋЏ',
- ],
- invalid: [
- 'řiď ',
- 'blé!!',
- 'föö!!',
- ],
- });
- });
-
- it('should validate serbian latin alphanumeric strings', function () {
- test({
- validator: 'isAlphanumeric',
- args: ['sr-RS@latin'],
- valid: [
- 'ŠAabčšđćž123',
- 'ŠATRO11Ćčđš',
- ],
- invalid: [
- 'řiď ',
- 'blé!!',
- 'föö!!',
- ],
- });
- });
-
- it('should validate swedish alphanumeric strings', function () {
- test({
- validator: 'isAlphanumeric',
- args: ['sv-SE'],
- valid: [
- 'religiös13',
- 'st23jäla',
- 'västgöte123',
- '123Åre',
- ],
- invalid: [
- 'AİıÖöÇ窺ĞğÜüZ',
- 'foo!!',
- '',
- ],
- });
- });
-
- it('should validate turkish alphanumeric strings', function () {
- test({
- validator: 'isAlphanumeric',
- args: ['tr-TR'],
- valid: [
- 'AİıÖöÇ窺ĞğÜüZ123',
- ],
- invalid: [
- 'AİıÖöÇ窺ĞğÜüZ ',
- 'foo!!',
- 'ÄBC',
- ],
- });
- });
-
- it('should validate urkrainian alphanumeric strings', function () {
- test({
- validator: 'isAlphanumeric',
- args: ['uk-UA'],
- valid: [
- 'АБВГҐДЕЄЖЗИIЇЙКЛМНОПРСТУФХЦШЩЬЮЯ123',
- ],
- invalid: [
- 'éeoc ',
- 'foo!!',
- 'ÄBC',
- 'ЫыЪъЭэ',
- ],
- });
- });
-
- it('should error on invalid locale', function () {
- try {
- validator.isAlphanumeric('abc123', 'in-INVALID');
- assert(false);
- } catch (err) {
- assert(true);
- }
- });
-
- it('should validate numeric strings', function () {
- test({
- validator: 'isNumeric',
- valid: [
- '123',
- '00123',
- '-00123',
- '0',
- '-0',
- '+123',
- ],
- invalid: [
- '123.123',
- ' ',
- '.',
- ],
- });
- });
-
- it('should validate ports', function () {
- test({
- validator: 'isPort',
- valid: [
- '0',
- '22',
- '80',
- '443',
- '3000',
- '8080',
- '65535',
- ],
- invalid: [
- '',
- '-1',
- '65536',
- ],
- });
- });
-
- it('should validate decimal numbers', function () {
- test({
- validator: 'isDecimal',
- valid: [
- '123',
- '00123',
- '-00123',
- '0',
- '-0',
- '+123',
- '0.01',
- '.1',
- '1.0',
- '-.25',
- '-0',
- '0.0000000000001',
- ],
- invalid: [
- '0,01',
- ',1',
- '1,0',
- '-,25',
- '0,0000000000001',
- '0٫01',
- '٫1',
- '1٫0',
- '-٫25',
- '0٫0000000000001',
- '....',
- ' ',
- '',
- '-',
- '+',
- '.',
- '0.1a',
- 'a',
- '\n',
- ],
- });
-
- test({
- validator: 'isDecimal',
- args: [{ locale: 'en-AU' }],
- valid: [
- '123',
- '00123',
- '-00123',
- '0',
- '-0',
- '+123',
- '0.01',
- '.1',
- '1.0',
- '-.25',
- '-0',
- '0.0000000000001',
- ],
- invalid: [
- '0,01',
- ',1',
- '1,0',
- '-,25',
- '0,0000000000001',
- '0٫01',
- '٫1',
- '1٫0',
- '-٫25',
- '0٫0000000000001',
- '....',
- ' ',
- '',
- '-',
- '+',
- '.',
- '0.1a',
- 'a',
- '\n',
- ],
- });
-
- test({
- validator: 'isDecimal',
- args: [{ locale: ['cs-CZ'] }],
- valid: [
- '123',
- '00123',
- '-00123',
- '0',
- '-0',
- '+123',
- '0,01',
- ',1',
- '1,0',
- '-,25',
- '-0',
- '0,0000000000001',
- ],
- invalid: [
- '0.0000000000001',
- '0.01',
- '.1',
- '1.0',
- '-.25',
- '0٫01',
- '٫1',
- '1٫0',
- '-٫25',
- '0٫0000000000001',
- '....',
- ' ',
- '',
- '-',
- '+',
- '.',
- '0.1a',
- 'a',
- '\n',
- ],
- });
-
- test({
- validator: 'isDecimal',
- args: [{ locale: ['ar-JO'] }],
- valid: [
- '123',
- '00123',
- '-00123',
- '0',
- '-0',
- '+123',
- '0٫01',
- '٫1',
- '1٫0',
- '-٫25',
- '-0',
- '0٫0000000000001',
- ],
- invalid: [
- '0,0000000000001',
- '0,01',
- ',1',
- '1,0',
- '-,25',
- '0.0000000000001',
- '0.01',
- '.1',
- '1.0',
- '-.25',
- '....',
- ' ',
- '',
- '-',
- '+',
- '.',
- '0.1a',
- 'a',
- '\n',
- ],
- });
-
- test({
- validator: 'isDecimal',
- args: [{ force_decimal: true }],
- valid: [
- '0.01',
- '.1',
- '1.0',
- '-.25',
- '0.0000000000001',
- ],
- invalid: [
- '-0',
- '123',
- '00123',
- '-00123',
- '0',
- '-0',
- '+123',
- '0,0000000000001',
- '0,01',
- ',1',
- '1,0',
- '-,25',
- '....',
- ' ',
- '',
- '-',
- '+',
- '.',
- '0.1a',
- 'a',
- '\n',
- ],
- });
-
- test({
- validator: 'isDecimal',
- args: [{ decimal_digits: '2,3' }],
- valid: [
- '123',
- '00123',
- '-00123',
- '0',
- '-0',
- '+123',
- '0.01',
- '1.043',
- '.15',
- '-.255',
- '-0',
- ],
- invalid: [
- '0.0000000000001',
- '0.0',
- '.1',
- '1.0',
- '-.2564',
- '0.0',
- '٫1',
- '1٫0',
- '-٫25',
- '0٫0000000000001',
- '....',
- ' ',
- '',
- '-',
- '+',
- '.',
- '0.1a',
- 'a',
- '\n',
- ],
- });
- });
-
- it('should validate lowercase strings', function () {
- test({
- validator: 'isLowercase',
- valid: [
- 'abc',
- 'abc123',
- 'this is lowercase.',
- 'tr竪s 端ber',
- ],
- invalid: [
- 'fooBar',
- '123A',
- ],
- });
- });
-
- it('should validate uppercase strings', function () {
- test({
- validator: 'isUppercase',
- valid: [
- 'ABC',
- 'ABC123',
- 'ALL CAPS IS FUN.',
- ' .',
- ],
- invalid: [
- 'fooBar',
- '123abc',
- ],
- });
- });
-
- it('should validate integers', function () {
- test({
- validator: 'isInt',
- valid: [
- '13',
- '123',
- '0',
- '123',
- '-0',
- '+1',
- '01',
- '-01',
- '000',
- ],
- invalid: [
- '100e10',
- '123.123',
- ' ',
- '',
- ],
- });
- test({
- validator: 'isInt',
- args: [{ allow_leading_zeroes: false }],
- valid: [
- '13',
- '123',
- '0',
- '123',
- '-0',
- '+1',
- ],
- invalid: [
- '01',
- '-01',
- '000',
- '100e10',
- '123.123',
- ' ',
- '',
- ],
- });
- test({
- validator: 'isInt',
- args: [{ allow_leading_zeroes: true }],
- valid: [
- '13',
- '123',
- '0',
- '123',
- '-0',
- '+1',
- '01',
- '-01',
- '000',
- '-000',
- '+000',
- ],
- invalid: [
- '100e10',
- '123.123',
- ' ',
- '',
- ],
- });
- test({
- validator: 'isInt',
- args: [{
- min: 10,
- }],
- valid: [
- '15',
- '80',
- '99',
- ],
- invalid: [
- '9',
- '6',
- '3.2',
- 'a',
- ],
- });
- test({
- validator: 'isInt',
- args: [{
- min: 10,
- max: 15,
- }],
- valid: [
- '15',
- '11',
- '13',
- ],
- invalid: [
- '9',
- '2',
- '17',
- '3.2',
- '33',
- 'a',
- ],
- });
- test({
- validator: 'isInt',
- args: [{
- gt: 10,
- lt: 15,
- }],
- valid: [
- '14',
- '11',
- '13',
- ],
- invalid: [
- '10',
- '15',
- '17',
- '3.2',
- '33',
- 'a',
- ],
- });
- });
-
- it('should validate floats', function () {
- test({
- validator: 'isFloat',
- valid: [
- '123',
- '123.',
- '123.123',
- '-123.123',
- '-0.123',
- '+0.123',
- '0.123',
- '.0',
- '-.123',
- '+.123',
- '01.123',
- '-0.22250738585072011e-307',
- ],
- invalid: [
- '+',
- '-',
- ' ',
- '',
- '.',
- 'foo',
- ],
- });
-
- test({
- validator: 'isFloat',
- args: [{ locale: 'en-AU' }],
- valid: [
- '123',
- '123.',
- '123.123',
- '-123.123',
- '-0.123',
- '+0.123',
- '0.123',
- '.0',
- '-.123',
- '+.123',
- '01.123',
- '-0.22250738585072011e-307',
- ],
- invalid: [
- '123٫123',
- '123,123',
- ' ',
- '',
- '.',
- 'foo',
- ],
- });
-
- test({
- validator: 'isFloat',
- args: [{ locale: 'de-DE' }],
- valid: [
- '123',
- '123,',
- '123,123',
- '-123,123',
- '-0,123',
- '+0,123',
- '0,123',
- ',0',
- '-,123',
- '+,123',
- '01,123',
- '-0,22250738585072011e-307',
- ],
- invalid: [
- '123.123',
- '123٫123',
- ' ',
- '',
- '.',
- 'foo',
- ],
- });
-
- test({
- validator: 'isFloat',
- args: [{ locale: 'ar-JO' }],
- valid: [
- '123',
- '123٫',
- '123٫123',
- '-123٫123',
- '-0٫123',
- '+0٫123',
- '0٫123',
- '٫0',
- '-٫123',
- '+٫123',
- '01٫123',
- '-0٫22250738585072011e-307',
- ],
- invalid: [
- '123,123',
- '123.123',
- ' ',
- '',
- '.',
- 'foo',
- ],
- });
-
- test({
- validator: 'isFloat',
- args: [{
- min: 3.7,
- }],
- valid: [
- '3.888',
- '3.92',
- '4.5',
- '50',
- '3.7',
- '3.71',
- ],
- invalid: [
- '3.6',
- '3.69',
- '3',
- '1.5',
- 'a',
- ],
- });
- test({
- validator: 'isFloat',
- args: [{
- min: 0.1,
- max: 1.0,
- }],
- valid: [
- '0.1',
- '1.0',
- '0.15',
- '0.33',
- '0.57',
- '0.7',
- ],
- invalid: [
- '0',
- '0.0',
- 'a',
- '1.3',
- '0.05',
- '5',
- ],
- });
- test({
- validator: 'isFloat',
- args: [{
- gt: -5.5,
- lt: 10,
- }],
- valid: [
- '9.9',
- '1.0',
- '0',
- '-1',
- '7',
- '-5.4',
- ],
- invalid: [
- '10',
- '-5.5',
- 'a',
- '-20.3',
- '20e3',
- '10.00001',
- ],
- });
- test({
- validator: 'isFloat',
- args: [{
- min: -5.5,
- max: 10,
- gt: -5.5,
- lt: 10,
- }],
- valid: [
- '9.99999',
- '-5.499999',
- ],
- invalid: [
- '10',
- '-5.5',
- ],
- });
- });
-
- it('should validate hexadecimal strings', function () {
- test({
- validator: 'isHexadecimal',
- valid: [
- 'deadBEEF',
- 'ff0044',
- ],
- invalid: [
- 'abcdefg',
- '',
- '..',
- ],
- });
- });
-
- it('should validate hexadecimal color strings', function () {
- test({
- validator: 'isHexColor',
- valid: [
- '#ff0034',
- '#CCCCCC',
- 'fff',
- '#f00',
- ],
- invalid: [
- '#ff',
- 'fff0',
- '#ff12FG',
- ],
- });
- });
-
- it('should validate ISRC code strings', function () {
- test({
- validator: 'isISRC',
- valid: [
- 'USAT29900609',
- 'GBAYE6800011',
- 'USRC15705223',
- 'USCA29500702',
- ],
- invalid: [
- 'USAT2990060',
- 'SRC15705223',
- 'US-CA29500702',
- 'USARC15705223',
- ],
- });
- });
-
- it('should validate md5 strings', function () {
- test({
- validator: 'isMD5',
- valid: [
- 'd94f3f016ae679c3008de268209132f2',
- '751adbc511ccbe8edf23d486fa4581cd',
- '88dae00e614d8f24cfd5a8b3f8002e93',
- '0bf1c35032a71a14c2f719e5a14c1e96',
- ],
- invalid: [
- 'KYT0bf1c35032a71a14c2f719e5a14c1',
- 'q94375dj93458w34',
- '39485729348',
- '%&FHKJFvk',
- ],
- });
- });
-
- it('should validate hash strings', function () {
- test({
- validator: 'isHash',
- args: ['md5', 'md4', 'ripemd128', 'tiger128'],
- valid: [
- 'd94f3f016ae679c3008de268209132f2',
- '751adbc511ccbe8edf23d486fa4581cd',
- '88dae00e614d8f24cfd5a8b3f8002e93',
- '0bf1c35032a71a14c2f719e5a14c1e96',
- ],
- invalid: [
- 'KYT0bf1c35032a71a14c2f719e5a14c1',
- 'q94375dj93458w34',
- '39485729348',
- '%&FHKJFvk',
- ],
- });
- test({
- validator: 'isHash',
- args: ['crc32', 'crc32b'],
- valid: [
- 'd94f3f01',
- '751adbc5',
- '88dae00e',
- '0bf1c350',
- ],
- invalid: [
- 'KYT0bf1c35032a71a14c2f719e5a14c1',
- 'q94375dj93458w34',
- 'q943',
- '39485729348',
- '%&FHKJFvk',
- ],
- });
- test({
- validator: 'isHash',
- args: ['sha1', 'tiger160', 'ripemd160'],
- valid: [
- '3ca25ae354e192b26879f651a51d92aa8a34d8d3',
- 'aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d',
- 'beb8c3f30da46be179b8df5f5ecb5e4b10508230',
- 'efd5d3b190e893ed317f38da2420d63b7ae0d5ed',
- ],
- invalid: [
- 'KYT0bf1c35032a71a14c2f719e5a14c1',
- 'KYT0bf1c35032a71a14c2f719e5a14c1dsjkjkjkjkkjk',
- 'q94375dj93458w34',
- '39485729348',
- '%&FHKJFvk',
- ],
- });
- test({
- validator: 'isHash',
- args: ['sha256'],
- valid: [
- '2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824',
- '1d996e033d612d9af2b44b70061ee0e868bfd14c2dd90b129e1edeb7953e7985',
- '80f70bfeaed5886e33536bcfa8c05c60afef5a0e48f699a7912d5e399cdcc441',
- '579282cfb65ca1f109b78536effaf621b853c9f7079664a3fbe2b519f435898c',
- ],
- invalid: [
- 'KYT0bf1c35032a71a14c2f719e5a14c1',
- 'KYT0bf1c35032a71a14c2f719e5a14c1dsjkjkjkjkkjk',
- 'q94375dj93458w34',
- '39485729348',
- '%&FHKJFvk',
- ],
- });
- test({
- validator: 'isHash',
- args: ['sha384'],
- valid: [
- '3fed1f814d28dc5d63e313f8a601ecc4836d1662a19365cbdcf6870f6b56388850b58043f7ebf2418abb8f39c3a42e31',
- 'b330f4e575db6e73500bd3b805db1a84b5a034e5d21f0041d91eec85af1dfcb13e40bb1c4d36a72487e048ac6af74b58',
- 'bf547c3fc5841a377eb1519c2890344dbab15c40ae4150b4b34443d2212e5b04aa9d58865bf03d8ae27840fef430b891',
- 'fc09a3d11368386530f985dacddd026ae1e44e0e297c805c3429d50744e6237eb4417c20ffca8807b071823af13a3f65',
- ],
- invalid: [
- 'KYT0bf1c35032a71a14c2f719e5a14c1',
- 'KYT0bf1c35032a71a14c2f719e5a14c1dsjkjkjkjkkjk',
- 'q94375dj93458w34',
- '39485729348',
- '%&FHKJFvk',
- ],
- });
- test({
- validator: 'isHash',
- args: ['sha512'],
- valid: [
- '9b71d224bd62f3785d96d46ad3ea3d73319bfbc2890caadae2dff72519673ca72323c3d99ba5c11d7c7acc6e14b8c5da0c4663475c2e5c3adef46f73bcdec043',
- '83c586381bf5ba94c8d9ba8b6b92beb0997d76c257708742a6c26d1b7cbb9269af92d527419d5b8475f2bb6686d2f92a6649b7f174c1d8306eb335e585ab5049',
- '45bc5fa8cb45ee408c04b6269e9f1e1c17090c5ce26ffeeda2af097735b29953ce547e40ff3ad0d120e5361cc5f9cee35ea91ecd4077f3f589b4d439168f91b9',
- '432ac3d29e4f18c7f604f7c3c96369a6c5c61fc09bf77880548239baffd61636d42ed374f41c261e424d20d98e320e812a6d52865be059745fdb2cb20acff0ab',
- ],
- invalid: [
- 'KYT0bf1c35032a71a14c2f719e5a14c1',
- 'KYT0bf1c35032a71a14c2f719e5a14c1dsjkjkjkjkkjk',
- 'q94375dj93458w34',
- '39485729348',
- '%&FHKJFvk',
- ],
- });
- test({
- validator: 'isHash',
- args: ['tiger192'],
- valid: [
- '6281a1f098c5e7290927ed09150d43ff3990a0fe1a48267c',
- '56268f7bc269cf1bc83d3ce42e07a85632394737918f4760',
- '46fc0125a148788a3ac1d649566fc04eb84a746f1a6e4fa7',
- '7731ea1621ae99ea3197b94583d034fdbaa4dce31a67404a',
- ],
- invalid: [
- 'KYT0bf1c35032a71a14c2f719e5a14c1',
- 'KYT0bf1c35032a71a14c2f719e5a14c1dsjkjkjkjkkjk',
- 'q94375dj93458w34',
- '39485729348',
- '%&FHKJFvk',
- ],
- });
- });
-
- it('should validate null strings', function () {
- test({
- validator: 'isEmpty',
- valid: [
- '',
- ],
- invalid: [
- ' ',
- 'foo',
- '3',
- ],
- });
- });
-
- it('should validate strings against an expected value', function () {
- test({ validator: 'equals', args: ['abc'], valid: ['abc'], invalid: ['Abc', '123'] });
- });
-
- it('should validate strings contain another string', function () {
- test({
- validator: 'contains',
- args: ['foo'],
- valid: ['foo', 'foobar', 'bazfoo'],
- invalid: ['bar', 'fobar'],
- });
- });
-
- it('should validate strings against a pattern', function () {
- test({
- validator: 'matches',
- args: [/abc/],
- valid: ['abc', 'abcdef', '123abc'],
- invalid: ['acb', 'Abc'],
- });
- test({
- validator: 'matches',
- args: ['abc'],
- valid: ['abc', 'abcdef', '123abc'],
- invalid: ['acb', 'Abc'],
- });
- test({
- validator: 'matches',
- args: ['abc', 'i'],
- valid: ['abc', 'abcdef', '123abc', 'AbC'],
- invalid: ['acb'],
- });
- });
-
- it('should validate strings by length (deprecated api)', function () {
- test({
- validator: 'isLength',
- args: [2],
- valid: ['abc', 'de', 'abcd'],
- invalid: ['', 'a'],
- });
- test({
- validator: 'isLength',
- args: [2, 3],
- valid: ['abc', 'de'],
- invalid: ['', 'a', 'abcd'],
- });
- test({
- validator: 'isLength',
- args: [2, 3],
- valid: ['干𩸽', '𠮷野家'],
- invalid: ['', '𠀋', '千竈通り'],
- });
- test({
- validator: 'isLength',
- args: [0, 0],
- valid: [''],
- invalid: ['a', 'ab'],
- });
- });
-
- it('should validate strings by byte length (deprecated api)', function () {
- test({
- validator: 'isByteLength',
- args: [2],
- valid: ['abc', 'de', 'abcd', 'gmail'],
- invalid: ['', 'a'],
- });
- test({
- validator: 'isByteLength',
- args: [2, 3],
- valid: ['abc', 'de', 'g'],
- invalid: ['', 'a', 'abcd', 'gm'],
- });
- test({
- validator: 'isByteLength',
- args: [0, 0],
- valid: [''],
- invalid: ['g', 'a'],
- });
- });
-
- it('should validate strings by length', function () {
- test({
- validator: 'isLength',
- args: [{ min: 2 }],
- valid: ['abc', 'de', 'abcd'],
- invalid: ['', 'a'],
- });
- test({
- validator: 'isLength',
- args: [{ min: 2, max: 3 }],
- valid: ['abc', 'de'],
- invalid: ['', 'a', 'abcd'],
- });
- test({
- validator: 'isLength',
- args: [{ min: 2, max: 3 }],
- valid: ['干𩸽', '𠮷野家'],
- invalid: ['', '𠀋', '千竈通り'],
- });
- test({
- validator: 'isLength',
- args: [{ max: 3 }],
- valid: ['abc', 'de', 'a', ''],
- invalid: ['abcd'],
- });
- test({
- validator: 'isLength',
- args: [{ max: 0 }],
- valid: [''],
- invalid: ['a', 'ab'],
- });
- });
-
- it('should validate strings by byte length', function () {
- test({
- validator: 'isByteLength',
- args: [{ min: 2 }],
- valid: ['abc', 'de', 'abcd', 'gmail'],
- invalid: ['', 'a'],
- });
- test({
- validator: 'isByteLength',
- args: [{ min: 2, max: 3 }],
- valid: ['abc', 'de', 'g'],
- invalid: ['', 'a', 'abcd', 'gm'],
- });
- test({
- validator: 'isByteLength',
- args: [{ max: 3 }],
- valid: ['abc', 'de', 'g', 'a', ''],
- invalid: ['abcd', 'gm'],
- });
- test({
- validator: 'isByteLength',
- args: [{ max: 0 }],
- valid: [''],
- invalid: ['g', 'a'],
- });
- });
-
- it('should validate UUIDs', function () {
- test({
- validator: 'isUUID',
- valid: [
- 'A987FBC9-4BED-3078-CF07-9141BA07C9F3',
- 'A987FBC9-4BED-4078-8F07-9141BA07C9F3',
- 'A987FBC9-4BED-5078-AF07-9141BA07C9F3',
- ],
- invalid: [
- '',
- 'xxxA987FBC9-4BED-3078-CF07-9141BA07C9F3',
- 'A987FBC9-4BED-3078-CF07-9141BA07C9F3xxx',
- 'A987FBC94BED3078CF079141BA07C9F3',
- '934859',
- '987FBC9-4BED-3078-CF07A-9141BA07C9F3',
- 'AAAAAAAA-1111-1111-AAAG-111111111111',
- ],
- });
- test({
- validator: 'isUUID',
- args: [3],
- valid: [
- 'A987FBC9-4BED-3078-CF07-9141BA07C9F3',
- ],
- invalid: [
- '',
- 'xxxA987FBC9-4BED-3078-CF07-9141BA07C9F3',
- '934859',
- 'AAAAAAAA-1111-1111-AAAG-111111111111',
- 'A987FBC9-4BED-4078-8F07-9141BA07C9F3',
- 'A987FBC9-4BED-5078-AF07-9141BA07C9F3',
- ],
- });
- test({
- validator: 'isUUID',
- args: [4],
- valid: [
- '713ae7e3-cb32-45f9-adcb-7c4fa86b90c1',
- '625e63f3-58f5-40b7-83a1-a72ad31acffb',
- '57b73598-8764-4ad0-a76a-679bb6640eb1',
- '9c858901-8a57-4791-81fe-4c455b099bc9',
- ],
- invalid: [
- '',
- 'xxxA987FBC9-4BED-3078-CF07-9141BA07C9F3',
- '934859',
- 'AAAAAAAA-1111-1111-AAAG-111111111111',
- 'A987FBC9-4BED-5078-AF07-9141BA07C9F3',
- 'A987FBC9-4BED-3078-CF07-9141BA07C9F3',
- ],
- });
- test({
- validator: 'isUUID',
- args: [5],
- valid: [
- '987FBC97-4BED-5078-AF07-9141BA07C9F3',
- '987FBC97-4BED-5078-BF07-9141BA07C9F3',
- '987FBC97-4BED-5078-8F07-9141BA07C9F3',
- '987FBC97-4BED-5078-9F07-9141BA07C9F3',
- ],
- invalid: [
- '',
- 'xxxA987FBC9-4BED-3078-CF07-9141BA07C9F3',
- '934859',
- 'AAAAAAAA-1111-1111-AAAG-111111111111',
- '9c858901-8a57-4791-81fe-4c455b099bc9',
- 'A987FBC9-4BED-3078-CF07-9141BA07C9F3',
- ],
- });
- });
-
- it('should validate a string that is in another string or array', function () {
- test({
- validator: 'isIn',
- args: ['foobar'],
- valid: ['foo', 'bar', 'foobar', ''],
- invalid: ['foobarbaz', 'barfoo'],
- });
- test({
- validator: 'isIn',
- args: [['foo', 'bar']],
- valid: ['foo', 'bar'],
- invalid: ['foobar', 'barfoo', ''],
- });
- test({
- validator: 'isIn',
- args: [['1', '2', '3']],
- valid: ['1', '2', '3'],
- invalid: ['4', ''],
- });
- test({ validator: 'isIn', invalid: ['foo', ''] });
- });
-
- it('should validate a string that is in another object', function () {
- test({
- validator: 'isIn',
- args: [{ 'foo': 1, 'bar': 2, 'foobar': 3 }],
- valid: ['foo', 'bar', 'foobar'],
- invalid: ['foobarbaz', 'barfoo', ''],
- });
- test({
- validator: 'isIn',
- args: [{ 1: 3, 2: 0, 3: 1 }],
- valid: ['1', '2', '3'],
- invalid: ['4', ''],
- });
- });
-
- it('should validate dates against a start date', function () {
- test({
- validator: 'isAfter',
- args: ['2011-08-03'],
- valid: ['2011-08-04', new Date(2011, 8, 10).toString()],
- invalid: ['2010-07-02', '2011-08-03', new Date(0).toString(), 'foo'],
- });
- test({
- validator: 'isAfter',
- valid: ['2100-08-04', new Date(Date.now() + 86400000).toString()],
- invalid: ['2010-07-02', new Date(0).toString()],
- });
- test({
- validator: 'isAfter',
- args: ['2011-08-03'],
- valid: ['2015-09-17'],
- invalid: ['invalid date'],
- });
- test({
- validator: 'isAfter',
- args: ['invalid date'],
- invalid: ['invalid date', '2015-09-17'],
- });
- });
-
- it('should validate dates against an end date', function () {
- test({
- validator: 'isBefore',
- args: ['08/04/2011'],
- valid: ['2010-07-02', '2010-08-04', new Date(0).toString()],
- invalid: ['08/04/2011', new Date(2011, 9, 10).toString()],
- });
- test({
- validator: 'isBefore',
- args: [new Date(2011, 7, 4).toString()],
- valid: ['2010-07-02', '2010-08-04', new Date(0).toString()],
- invalid: ['08/04/2011', new Date(2011, 9, 10).toString()],
- });
- test({
- validator: 'isBefore',
- valid: [
- '2000-08-04',
- new Date(0).toString(),
- new Date(Date.now() - 86400000).toString(),
- ],
- invalid: ['2100-07-02', new Date(2217, 10, 10).toString()],
- });
- test({
- validator: 'isBefore',
- args: ['2011-08-03'],
- valid: ['1999-12-31'],
- invalid: ['invalid date'],
- });
- test({
- validator: 'isBefore',
- args: ['invalid date'],
- invalid: ['invalid date', '1999-12-31'],
- });
- });
-
- it('should validate that integer strings are divisible by a number', function () {
- test({
- validator: 'isDivisibleBy',
- args: [2],
- valid: ['2', '4', '100', '1000'],
- invalid: [
- '1',
- '2.5',
- '101',
- 'foo',
- '',
- ],
- });
- });
-
- it('should validate credit cards', function () {
- test({
- validator: 'isCreditCard',
- valid: [
- '375556917985515',
- '36050234196908',
- '4716461583322103',
- '4716-2210-5188-5662',
- '4929 7226 5379 7141',
- '5398228707871527',
- '6283875070985593',
- '6263892624162870',
- '6234917882863855',
- '6234698580215388',
- '6226050967750613',
- '6246281879460688',
- '2222155765072228',
- '2225855203075256',
- '2720428011723762',
- '2718760626256570',
- ],
- invalid: [
- 'foo',
- 'foo',
- '5398228707871528',
- '2718760626256571',
- '2721465526338453',
- '2220175103860763',
- '375556917985515999999993',
- '899999996234917882863855',
- 'prefix6234917882863855',
- '623491788middle2863855',
- '6234917882863855suffix',
- ],
- });
- });
-
- it('should validate ISINs', function () {
- test({
- validator: 'isISIN',
- valid: [
- 'AU0000XVGZA3',
- 'DE000BAY0017',
- 'BE0003796134',
- 'SG1G55870362',
- 'GB0001411924',
- 'DE000WCH8881',
- 'PLLWBGD00016',
- ],
- invalid: [
- 'DE000BAY0018',
- 'PLLWBGD00019',
- 'foo',
- '5398228707871528',
- ],
- });
- });
-
- it('should validate ISBNs', function () {
- test({
- validator: 'isISBN',
- args: [10],
- valid: [
- '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',
- ],
- invalid: [
- '3423214121', '3-423-21412-1', '3 423 21412 1',
- '978-3836221191', '9783836221191',
- '123456789a', 'foo', '',
- ],
- });
- test({
- validator: 'isISBN',
- args: [13],
- valid: [
- '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',
- ],
- invalid: [
- '9783836221190', '978-3-8362-2119-0', '978 3 8362 2119 0',
- '3836221195', '3-8362-2119-5', '3 8362 2119 5',
- '01234567890ab', 'foo', '',
- ],
- });
- test({
- validator: 'isISBN',
- valid: [
- '340101319X',
- '9784873113685',
- ],
- invalid: [
- '3423214121',
- '9783836221190',
- ],
- });
- test({
- validator: 'isISBN',
- args: ['foo'],
- invalid: [
- '340101319X',
- '9784873113685',
- ],
- });
- });
-
- it('should validate ISSNs', function () {
- test({
- validator: 'isISSN',
- valid: [
- '0378-5955',
- '0000-0000',
- '2434-561X',
- '2434-561x',
- '01896016',
- '20905076',
- ],
- invalid: [
- '0378-5954',
- '0000-0001',
- '0378-123',
- '037-1234',
- '0',
- '2434-561c',
- '1684-5370',
- '19960791',
- '',
- ],
- });
- test({
- validator: 'isISSN',
- args: [{ case_sensitive: true }],
- valid: [
- '2434-561X',
- '2434561X',
- '0378-5955',
- '03785955',
- ],
- invalid: [
- '2434-561x',
- '2434561x',
- ],
- });
- test({
- validator: 'isISSN',
- args: [{ require_hyphen: true }],
- valid: [
- '2434-561X',
- '2434-561x',
- '0378-5955',
- ],
- invalid: [
- '2434561X',
- '2434561x',
- '03785955',
- ],
- });
- test({
- validator: 'isISSN',
- args: [{ case_sensitive: true, require_hyphen: true }],
- valid: [
- '2434-561X',
- '0378-5955',
- ],
- invalid: [
- '2434-561x',
- '2434561X',
- '2434561x',
- '03785955',
- ],
- });
- });
-
- it('should validate JSON', function () {
- test({
- validator: 'isJSON',
- valid: [
- '{ "key": "value" }',
- '{}',
- ],
- invalid: [
- '{ key: "value" }',
- '{ \'key\': \'value\' }',
- 'null',
- '1234',
- 'false',
- '"nope"',
- ],
- });
- });
-
- it('should validate multibyte strings', function () {
- test({
- validator: 'isMultibyte',
- valid: [
- 'ひらがな・カタカナ、.漢字',
- 'あいうえお foobar',
- 'test@example.com',
- '1234abcDExyz',
- 'カタカナ',
- '中文',
- ],
- invalid: [
- 'abc',
- 'abc123',
- '<>@" *.',
- ],
- });
- });
-
- it('should validate ascii strings', function () {
- test({
- validator: 'isAscii',
- valid: [
- 'foobar',
- '0987654321',
- 'test@example.com',
- '1234abcDEF',
- ],
- invalid: [
- 'foobar',
- 'xyz098',
- '123456',
- 'カタカナ',
- ],
- });
- });
-
- it('should validate full-width strings', function () {
- test({
- validator: 'isFullWidth',
- valid: [
- 'ひらがな・カタカナ、.漢字',
- '3ー0 a@com',
- 'Fカタカナ゙ᆲ',
- 'Good=Parts',
- ],
- invalid: [
- 'abc',
- 'abc123',
- '!"#$%&()<>/+=-_? ~^|.,@`{}[]',
- ],
- });
- });
-
- it('should validate half-width strings', function () {
- test({
- validator: 'isHalfWidth',
- valid: [
- '!"#$%&()<>/+=-_? ~^|.,@`{}[]',
- 'l-btn_02--active',
- 'abc123い',
- 'カタカナ゙ᆲ←',
- ],
- invalid: [
- 'あいうえお',
- '0011',
- ],
- });
- });
-
- it('should validate variable-width strings', function () {
- test({
- validator: 'isVariableWidth',
- valid: [
- 'ひらがなカタカナ漢字ABCDE',
- '3ー0123',
- 'Fカタカナ゙ᆲ',
- 'Good=Parts',
- ],
- invalid: [
- 'abc',
- 'abc123',
- '!"#$%&()<>/+=-_? ~^|.,@`{}[]',
- 'ひらがな・カタカナ、.漢字',
- '123456',
- 'カタカナ゙ᆲ',
- ],
- });
- });
-
- it('should validate surrogate pair strings', function () {
- test({
- validator: 'isSurrogatePair',
- valid: [
- '𠮷野𠮷',
- '𩸽',
- 'ABC千𥧄1-2-3',
- ],
- invalid: [
- '吉野竈',
- '鮪',
- 'ABC1-2-3',
- ],
- });
- });
-
- it('should validate base64 strings', function () {
- test({
- validator: 'isBase64',
- valid: [
- 'Zg==',
- 'Zm8=',
- 'Zm9v',
- 'Zm9vYg==',
- 'Zm9vYmE=',
- 'Zm9vYmFy',
- 'TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdC4=',
- 'Vml2YW11cyBmZXJtZW50dW0gc2VtcGVyIHBvcnRhLg==',
- 'U3VzcGVuZGlzc2UgbGVjdHVzIGxlbw==',
- 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuMPNS1Ufof9EW/M98FNw' +
- 'UAKrwflsqVxaxQjBQnHQmiI7Vac40t8x7pIb8gLGV6wL7sBTJiPovJ0V7y7oc0Ye' +
- 'rhKh0Rm4skP2z/jHwwZICgGzBvA0rH8xlhUiTvcwDCJ0kc+fh35hNt8srZQM4619' +
- 'FTgB66Xmp4EtVyhpQV+t02g6NzK72oZI0vnAvqhpkxLeLiMCyrI416wHm5Tkukhx' +
- 'QmcL2a6hNOyu0ixX/x2kSFXApEnVrJ+/IxGyfyw8kf4N2IZpW5nEP847lpfj0SZZ' +
- 'Fwrd1mnfnDbYohX2zRptLy2ZUn06Qo9pkG5ntvFEPo9bfZeULtjYzIl6K8gJ2uGZ' +
- 'HQIDAQAB',
- ],
- invalid: [
- '12345',
- '',
- 'Vml2YW11cyBmZXJtZtesting123',
- 'Zg=',
- 'Z===',
- 'Zm=8',
- '=m9vYg==',
- 'Zm9vYmFy====',
- ],
- });
- for (var i = 0, str = '', encoded; i < 1000; i++) {
- str += String.fromCharCode(Math.random() * 26 | 97);
- encoded = new Buffer(str).toString('base64');
- if (!validator.isBase64(encoded)) {
- var msg = format('validator.isBase64() failed with "%s"', encoded);
- throw new Error(msg);
- }
- }
- });
-
- it('should validate hex-encoded MongoDB ObjectId', function () {
- test({
- validator: 'isMongoId',
- valid: [
- '507f1f77bcf86cd799439011',
- ],
- invalid: [
- '507f1f77bcf86cd7994390',
- '507f1f77bcf86cd79943901z',
- '',
- '507f1f77bcf86cd799439011 ',
- ],
- });
- });
-
- it('should define the module using an AMD-compatible loader', function () {
- var window = {
- validator: null,
- define: function (module) {
- window.validator = module();
- },
- };
- window.define.amd = true;
-
- var sandbox = vm.createContext(window);
- vm.runInContext(validator_js, sandbox);
- assert.equal(window.validator.trim(' foobar '), 'foobar');
- });
-
- it('should bind validator to the window if no module loaders are available', function () {
- var window = {};
- var sandbox = vm.createContext(window);
- vm.runInContext(validator_js, sandbox);
- assert.equal(window.validator.trim(' foobar '), 'foobar');
- });
-
- it('should validate mobile phone number', function () {
- var fixtures = [
- {
- locale: 'ar-AE',
- valid: [
- '+971502674453',
- '+971521247658',
- '+971541255684',
- '+971555454458',
- '+971561498855',
- '+971585215778',
- '971585215778',
- '0585215778',
- '585215778',
- ],
- invalid: [
- '12345',
- '+971511498855',
- '+9715614988556',
- '+9745614988556',
- '',
- '+9639626626262',
- '+963332210972',
- '0114152198',
- '962796477263',
- ],
- },
- {
- locale: 'ar-EG',
- valid: [
- '+201004513789',
- '+201111453489',
- '+201221204610',
- '+201144621154',
- '+201200124304',
- '+201011201564',
- '+201124679001',
- '+201064790156',
- '+201274652177',
- '+201280134679',
- '+201090124576',
- '201090124576',
- '01090124576',
- '1090124576',
- ],
- invalid: [
- '+221004513789',
- '+201404513789',
- '12345',
- '',
- '+9639626626262',
- '+963332210972',
- '0114152198',
- '962796477263',
- ],
- },
- {
- locale: 'ar-JO',
- valid: [
- '0796477263',
- '0777866254',
- '0786725261',
- '+962796477263',
- '+962777866254',
- '+962786725261',
- '962796477263',
- '962777866254',
- '962786725261',
- ],
- invalid: [
- '00962786725261',
- '00962796477263',
- '12345',
- '',
- '+9639626626262',
- '+963332210972',
- '0114152198',
- ],
- },
- {
- locale: 'ar-SY',
- valid: [
- '0944549710',
- '+963944549710',
- '956654379',
- '0944549710',
- '0962655597',
- ],
- invalid: [
- '12345',
- '',
- '+9639626626262',
- '+963332210972',
- '0114152198',
- ],
- },
- {
- locale: 'ar-SA',
- valid: [
- '0556578654',
- '+966556578654',
- '966556578654',
- '596578654',
- '572655597',
- ],
- invalid: [
- '12345',
- '',
- '+9665626626262',
- '+96633221097',
- '0114152198',
- ],
- },
- {
- locale: 'cs-CZ',
- valid: [
- '+420 123 456 789',
- '+420 123456789',
- '+420123456789',
- '123 456 789',
- '123456789',
- ],
- invalid: [
- '',
- '+42012345678',
- '+421 123 456 789',
- '+420 023456789',
- '+4201234567892',
- ],
- },
- {
- locale: 'sk-SK',
- valid: [
- '+421 123 456 789',
- '+421 123456789',
- '+421123456789',
- '123 456 789',
- '123456789',
- ],
- invalid: [
- '',
- '+42112345678',
- '+422 123 456 789',
- '+421 023456789',
- '+4211234567892',
- ],
- },
- {
- locale: 'de-DE',
- valid: [
- '+49 (0) 123 456 789',
- '+49 (0) 123 456789',
- '0123/4567890',
- '+49 01234567890',
- '01234567890',
- ],
- invalid: [
- '',
- 'Vml2YW11cyBmZXJtZtesting123',
- ],
- },
- {
- locale: 'pt-BR',
- valid: [
- '55-17-3332-2155',
- '55-15-25661234',
- '551223456789',
- '01523456987',
- '022995678947',
- '+55-12-996551215',
- ],
- invalid: [
- '+017-123456789',
- '5501599623874',
- '+55012962308',
- '+55-015-1234-3214',
- ],
- },
- {
- locale: 'zh-CN',
- valid: [
- '15323456787',
- '13523333233',
- '13898728332',
- '+086-13238234822',
- '08613487234567',
- '8617823492338',
- '86-17823492338',
- ],
- invalid: [
- '12345',
- '',
- 'Vml2YW11cyBmZXJtZtesting123',
- '010-38238383',
- ],
- },
- {
- locale: 'zh-TW',
- valid: [
- '0987123456',
- '+886987123456',
- '886987123456',
- '+886-987123456',
- '886-987123456',
- ],
- invalid: [
- '12345',
- '',
- 'Vml2YW11cyBmZXJtZtesting123',
- '0-987123456',
- ],
- },
- {
- locale: 'en-ZA',
- valid: [
- '0821231234',
- '+27821231234',
- '27821231234',
- ],
- invalid: [
- '082123',
- '08212312345',
- '21821231234',
- '+21821231234',
- '+0821231234',
- ],
- },
- {
- locale: 'en-AU',
- valid: [
- '61404111222',
- '+61411222333',
- '0417123456',
- ],
- invalid: [
- '082123',
- '08212312345',
- '21821231234',
- '+21821231234',
- '+0821231234',
- '04123456789',
- ],
- },
- {
- locale: 'en-HK',
- valid: [
- '91234567',
- '9123-4567',
- '61234567',
- '51234567',
- '+85291234567',
- '+852-91234567',
- '+852-9123-4567',
- '852-91234567',
- ],
- invalid: [
- '999',
- '+852-912345678',
- '123456789',
- '+852-1234-56789',
- ],
- },
- {
- locale: 'en-KE',
- valid: [
- '+254728590432',
- '+254733875610',
- '254728590234',
- '0733346543',
- '0700459022',
- ],
- invalid: [
- '999',
- '+25489032',
- '123456789',
- '+254800723845',
- ],
- },
- {
- locale: 'en-UG',
- valid: [
- '+256728590432',
- '+256733875610',
- '256728590234',
- '0773346543',
- '0700459022',
- ],
- invalid: [
- '999',
- '+254728590432',
- '+25489032',
- '123456789',
- '+254800723845',
- ],
- },
- {
- locale: 'en-RW',
- valid: [
- '+250728590432',
- '+250733875610',
- '250738590234',
- '0753346543',
- '0780459022',
- ],
- invalid: [
- '999',
- '+254728590432',
- '+25089032',
- '123456789',
- '+250800723845',
- ],
- },
- {
- locale: 'en-TZ',
- valid: [
- '+255728590432',
- '+255733875610',
- '255628590234',
- '0673346543',
- '0600459022',
- ],
- invalid: [
- '999',
- '+254728590432',
- '+25589032',
- '123456789',
- '+255800723845',
- ],
- },
- {
- locale: 'fr-FR',
- valid: [
- '0612457898',
- '+33612457898',
- '33612457898',
- '0712457898',
- '+33712457898',
- '33712457898',
- ],
- invalid: [
- '061245789',
- '06124578980',
- '0112457898',
- '0212457898',
- '0312457898',
- '0412457898',
- '0512457898',
- '0812457898',
- '0912457898',
- '+34612457898',
- '+336124578980',
- '+3361245789',
- ],
- },
- {
- locale: 'el-GR',
- valid: [
- '+306944848966',
- '6944848966',
- '306944848966',
- ],
- invalid: [
- '2102323234',
- '+302646041461',
- '120000000',
- '20000000000',
- '68129485729',
- '6589394827',
- '298RI89572',
- ],
- },
- {
- locale: 'en-GB',
- valid: [
- '447789345856',
- '+447861235675',
- '07888814488',
- ],
- invalid: [
- '67699567',
- '0773894868',
- '077389f8688',
- '+07888814488',
- '0152456999',
- '442073456754',
- '+443003434751',
- '05073456754',
- '08001123123',
- ],
- },
- {
- locale: 'en-SG',
- valid: [
- '87654321',
- '98765432',
- '+6587654321',
- '+6598765432',
- ],
- invalid: [
- '987654321',
- '876543219',
- '8765432',
- '9876543',
- '12345678',
- '+98765432',
- '+9876543212',
- '+15673628910',
- '19876543210',
- '8005552222',
- ],
- },
- {
- locale: 'en-US',
- valid: [
- '19876543210',
- '8005552222',
- '+15673628910',
- ],
- invalid: [
- '564785',
- '0123456789',
- '1437439210',
- '8009112340',
- '+10345672645',
- '11435213543',
- '2436119753',
- '16532116190',
- ],
- },
- {
- locale: 'en-CA',
- valid: [
- '19876543210',
- '8005552222',
- '+15673628910',
- ],
- invalid: [
- '564785',
- '0123456789',
- '1437439210',
- '8009112340',
- '+10345672645',
- '11435213543',
- '2436119753',
- '16532116190',
- ],
- },
- {
- locale: 'en-ZM',
- valid: [
- '0956684590',
- '0966684590',
- '0976684590',
- '+260956684590',
- '+260966684590',
- '+260976684590',
- '260976684590',
- ],
- invalid: [
- '12345',
- '',
- 'Vml2YW11cyBmZXJtZtesting123',
- '010-38238383',
- '966684590',
- ],
- },
- {
- locale: 'ru-RU',
- valid: [
- '+79676338855',
- '79676338855',
- '89676338855',
- '9676338855',
- ],
- invalid: [
- '12345',
- '',
- 'Vml2YW11cyBmZXJtZtesting123',
- '010-38238383',
- '+9676338855',
- '19676338855',
- '6676338855',
- '+99676338855',
- ],
- },
- {
- locale: 'sr-RS',
- valid: [
- '0640133338',
- '063333133',
- '0668888878',
- '+381645678912',
- '+381611314000',
- '0655885010',
- ],
- invalid: [
- '12345',
- '',
- 'Vml2YW11cyBmZXJtZtesting123',
- '010-38238383',
- '+9676338855',
- '19676338855',
- '6676338855',
- '+99676338855',
- ],
- },
- {
- locale: 'en-NZ',
- valid: [
- '+6427987035',
- '642240512347',
- '0293981646',
- '029968425',
- ],
- invalid: [
- '12345',
- '',
- 'Vml2YW11cyBmZXJtZtesting123',
- '+642956696123566',
- '+02119620856',
- '+9676338855',
- '19676338855',
- '6676338855',
- '+99676338855',
- ],
- },
- {
- locale: ['nb-NO', 'nn-NO'], // for multiple locales
- valid: [
- '+4796338855',
- '+4746338855',
- '4796338855',
- '4746338855',
- '46338855',
- '96338855',
- ],
- invalid: [
- '12345',
- '',
- 'Vml2YW11cyBmZXJtZtesting123',
- '+4676338855',
- '19676338855',
- '+4726338855',
- '4736338855',
- '66338855',
- ],
- },
- {
- locale: 'vi-VN',
- valid: [
- '01636012403',
- '+841636012403',
- '1636012403',
- '841636012403',
- '+84999999999',
- '84999999999',
- '0999999999',
- '999999999',
- ],
- invalid: [
- '12345',
- '',
- 'Vml2YW11cyBmZXJtZtesting123',
- '010-38238383',
- '260976684590',
- ],
- },
- {
- locale: 'es-ES',
- valid: [
- '+34654789321',
- '654789321',
- '+34714789321',
- '714789321',
- '+34744789321',
- '744789321',
- ],
- invalid: [
- '12345',
- '',
- 'Vml2YW11cyBmZXJtZtesting123',
- '+3465478932',
- '65478932',
- '+346547893210',
- '6547893210',
- '+34704789321',
- '704789321',
- '+34754789321',
- '754789321',
- ],
- },
- {
- locale: 'et-EE',
- valid: [
- '+372 512 34 567',
- '372 512 34 567',
- '+37251234567',
- '51234567',
- '81234567',
- '+372842345678',
- ],
- invalid: [
- '12345',
- '',
- 'NotANumber',
- '+333 51234567',
- '61234567',
- '+51234567',
- '+372 539 57 4',
- '+372 900 1234',
- '12345678',
- ],
- },
- {
- locale: 'pl-PL',
- valid: [
- '+48512689767',
- '+48 56 376 87 47',
- '56 566 78 46',
- '657562855',
- '+48657562855',
- '+48 887472765',
- '+48 56 6572724',
- '+48 67 621 5461',
- '48 67 621 5461',
- ],
- invalid: [
- '+48 67 621 5461',
- '+55657562855',
- '3454535',
- 'teststring',
- '',
- '1800-88-8687',
- '+6019-5830837',
- '357562855',
- ],
- },
- {
- locale: 'fa-IR',
- valid: [
- '+989123456789',
- '989223456789',
- '09323456789',
- '09021456789',
- '+98-990-345-6789',
- '+98 938 345 6789',
- '0938 345 6789',
- ],
- invalid: [
- '',
- '+989623456789',
- '+981123456789',
- '01234567890',
- '09423456789',
- '09823456789',
- '9123456789',
- '091234567890',
- '0912345678',
- '+98 912 3456 6789',
- '0912 345 678',
- ],
- },
- {
- locale: 'fi-FI',
- valid: [
- '+358505557171',
- '0455571',
- '0505557171',
- '358505557171',
- '04412345',
- '0457 123 45 67',
- '+358457 123 45 67',
- '+358 50 555 7171',
- ],
- invalid: [
- '12345',
- '',
- '045557',
- '045555717112312332423423421',
- 'Vml2YW11cyBmZXJtZtesting123',
- '010-38238383',
- '+3-585-0555-7171',
- '+9676338855',
- '19676338855',
- '6676338855',
- '+99676338855',
- '044123',
- '019123456789012345678901',
- ],
- },
- {
- locale: 'ms-MY',
- valid: [
- '+60128228789',
- '+60195830837',
- '+6019-5830837',
- '+6019-5830837',
- '0128737867',
- '01468987837',
- '016-2838768',
- '016 2838768',
- ],
- invalid: [
- '12345',
- '601238788657',
- '088387675',
- '16-2838768',
- '032551433',
- '6088-387888',
- '088-261987',
- '1800-88-8687',
- '088-320000',
- ],
- },
- {
- locale: 'ko-KR',
- valid: [
- '+82-010-1234-5678',
- '+82-10-1234-5678',
- '82-010-1234-5678',
- '82-10-1234-5678',
- '+82 10 1234 5678',
- '010-123-5678',
- '10-1234-5678',
- '+82 10 1234 5678',
- '011 1234 5678',
- '+820112345678',
- '01012345678',
- '+82 016 1234 5678',
- '82 19 1234 5678',
- '+82 010 12345678',
- ],
- invalid: [
- 'abcdefghi',
- '+82 10 1234 567',
- '+82 10o 1234 1234',
- '+82 101 1234 5678',
- '+82 10 12 5678',
- '+011 7766 1234',
- '011_7766_1234',
- '+820 11 7766 1234',
- ],
- },
- {
- locale: 'ja-JP',
- valid: [
- '09012345688',
- '090 123 45678',
- '+8190-123-45678',
- ],
- invalid: [
- '12345',
- '',
- '045555717112312332423423421',
- 'Vml2YW11cyBmZXJtZtesting123',
- '+3-585-0555-7171',
- '0 1234 5689',
- '16 1234 5689',
- '03_1234_5689',
- '0312345678',
- '0721234567',
- '08002345678',
- '06 1234 5678',
- '072 123 4567',
- '0729 12 3456',
- '07296 1 2345',
- '072961 2345',
- '03-1234-5678',
- '+81312345678',
- '+816-1234-5678',
- ],
- },
- {
- locale: 'it-IT',
- valid: [
- '370 3175423',
- '333202925',
- '+39 310 7688449',
- '+39 3339847632',
- ],
- invalid: [
- '011 7387545',
- '12345',
- '+45 345 6782395',
- ],
- },
- {
- locale: 'fr-BE',
- valid: [
- '0470123456',
- '+32470123456',
- '32470123456',
- '021234567',
- '+3221234567',
- '3221234567',
- ],
- invalid: [
- '12345',
- '+3212345',
- '3212345',
- '04701234567',
- '+3204701234567',
- '3204701234567',
- '0212345678',
- '+320212345678',
- '320212345678',
- ],
- },
- {
- locale: 'nl-BE',
- valid: [
- '0470123456',
- '+32470123456',
- '32470123456',
- '021234567',
- '+3221234567',
- '3221234567',
- ],
- invalid: [
- '12345',
- '+3212345',
- '3212345',
- '04701234567',
- '+3204701234567',
- '3204701234567',
- '0212345678',
- '+320212345678',
- '320212345678',
- ],
- },
- {
- locale: 'ro-RO',
- valid: [
- '+40740123456',
- '+40 740123456',
- '+40740 123 456',
- '+40740.123.456',
- '+40740-123-456',
- '40740123456',
- '40 740123456',
- '40740 123 456',
- '40740.123.456',
- '40740-123-456',
- '0740123456',
- '0740/123456',
- '0740 123 456',
- '0740.123.456',
- '0740-123-456',
- ],
- invalid: [
- '',
- 'Vml2YW11cyBmZXJtZtesting123',
- '123456',
- '740123456',
- '+40640123456',
- '+40210123456',
- ],
- },
- {
- locale: 'id-ID',
- valid: [
- '0217123456',
- '0811 778 998',
- '089931236181900',
- '622178878890',
- '62811 778 998',
- '62811778998',
- '6289931236181900',
- '6221 740123456',
- '62899 740123456',
- '62899 7401 2346',
- '0341 8123456',
- '0778 89800910',
- '0741 123456',
- '+6221740123456',
- '+62811 778 998',
- '+62811778998',
- ],
- invalid: [
- '+65740 123 456',
- '',
- 'ASDFGJKLmZXJtZtesting123',
- '123456',
- '740123456',
- '+65640123456',
- '+64210123456',
- ],
- },
- {
- locale: 'lt-LT',
- valid: [
- '+37051234567',
- '851234567',
- ],
- invalid: [
- '+65740 123 456',
- '',
- 'ASDFGJKLmZXJtZtesting123',
- '123456',
- '740123456',
- '+65640123456',
- '+64210123456',
- ],
- },
- {
- locale: 'uk-UA',
- valid: [
- '+380982345679',
- '380982345679',
- '80982345679',
- '0982345679',
- ],
- invalid: [
- '+30982345679',
- '982345679',
- '+380 98 234 5679',
- '+380-98-234-5679',
- '',
- 'ASDFGJKLmZXJtZtesting123',
- '123456',
- '740123456',
- ],
- },
- {
- locale: 'da-DK',
- valid: [
- '12345678',
- '12 34 56 78',
- '45 12345678',
- '4512345678',
- '45 12 34 56 78',
- '+45 12 34 56 78',
- ],
- invalid: [
- '',
- '+45010203',
- 'ASDFGJKLmZXJtZtesting123',
- '123456',
- '12 34 56',
- '123 123 12',
- ],
- },
- {
- locale: 'fo-FO',
- valid: [
- '123456',
- '12 34 56',
- '298 123456',
- '298123456',
- '298 12 34 56',
- '+298 12 34 56',
- ],
- invalid: [
- '',
- '+4501020304',
- 'ASDFGJKLmZXJtZtesting123',
- '12345678',
- '12 34 56 78',
- ],
- },
- {
- locale: 'kl-GL',
- valid: [
- '123456',
- '12 34 56',
- '299 123456',
- '299123456',
- '299 12 34 56',
- '+299 12 34 56',
- ],
- invalid: [
- '',
- '+4501020304',
- 'ASDFGJKLmZXJtZtesting123',
- '12345678',
- '12 34 56 78',
- ],
- },
- ];
-
- var allValid = [];
-
- fixtures.forEach(function (fixture) {
- // to be used later on for validating 'any' locale
- if (fixture.valid) allValid = allValid.concat(fixture.valid);
-
- if (Array.isArray(fixture.locale)) {
- // for fixtures that are shared across multiple locales
- // e.g. 'nb-NO' and 'nn-NO'
- fixture.locale.forEach(function (locale) {
- test({
- validator: 'isMobilePhone',
- valid: fixture.valid,
- invalid: fixture.invalid,
- args: [locale],
- });
- });
- } else {
- test({
- validator: 'isMobilePhone',
- valid: fixture.valid,
- invalid: fixture.invalid,
- args: [fixture.locale],
- });
- }
- });
-
- test({
- validator: 'isMobilePhone',
- valid: allValid,
- invalid: [
- '',
- 'asdf',
- '1',
- 'ASDFGJKLmZXJtZtesting123',
- 'Vml2YW11cyBmZXJtZtesting123',
- ],
- args: ['any'],
- });
- });
-
- it('should validate currency', function () {
- test({
- validator: 'isCurrency',
- args: [
- { },
- '-$##,###.## (en-US, en-CA, en-AU, en-NZ, en-HK)',
- ],
- valid: [
- '-$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',
- ],
- invalid: [
- '1.234',
- '$1.1',
- '$ 32.50',
- '500$',
- '.0001',
- '$.001',
- '$0.001',
- '12,34.56',
- '123456,123,123456',
- '123,4',
- ',123',
- '$-,123',
- '$',
- '.',
- ',',
- '00',
- '$-',
- '$-,.',
- '-',
- '-$',
- '',
- '- $',
- ],
- });
-
- test({
- validator: 'isCurrency',
- args: [
- {
- allow_decimal: false,
- },
- '-$##,###.## (en-US, en-CA, en-AU, en-NZ, en-HK)',
- ],
- valid: [
- '-$10,123',
- '$10,123',
- '$10123',
- '10,123',
- '10123',
- '10,123',
- '1,123,456',
- '1123456',
- '1',
- '0',
- '$0',
- '-$0',
- '$100,234,567',
- '$10,123',
- '10,123',
- '-10123',
- ],
- invalid: [
- '-$10,123.45',
- '$10,123.45',
- '$10123.45',
- '10,123.45',
- '10123.45',
- '1.39',
- '.03',
- '0.10',
- '$0.10',
- '-$0.01',
- '-$.99',
- '$100,234,567.89',
- '1.234',
- '$1.1',
- '$ 32.50',
- '.0001',
- '$.001',
- '$0.001',
- '12,34.56',
- '123,4',
- ',123',
- '$-,123',
- '$',
- '.',
- ',',
- '00',
- '$-',
- '$-,.',
- '-',
- '-$',
- '',
- '- $',
- ],
- });
-
- test({
- validator: 'isCurrency',
- args: [
- {
- require_decimal: true,
- },
- '-$##,###.## (en-US, en-CA, en-AU, en-NZ, en-HK)',
- ],
- valid: [
- '-$10,123.45',
- '$10,123.45',
- '$10123.45',
- '10,123.45',
- '10123.45',
- '10,123.00',
- '1.39',
- '.03',
- '0.10',
- '$0.10',
- '-$0.01',
- '-$.99',
- '$100,234,567.89',
- ],
- invalid: [
- '$10,123',
- '10,123',
- '-10123',
- '1,123,456',
- '1123456',
- '1.234',
- '$1.1',
- '$ 32.50',
- '500$',
- '.0001',
- '$.001',
- '$0.001',
- '12,34.56',
- '123456,123,123456',
- '123,4',
- ',123',
- '$-,123',
- '$',
- '.',
- ',',
- '00',
- '$-',
- '$-,.',
- '-',
- '-$',
- '',
- '- $',
- ],
- });
-
- test({
- validator: 'isCurrency',
- args: [
- {
- digits_after_decimal: [1, 3],
- },
- '-$##,###.## (en-US, en-CA, en-AU, en-NZ, en-HK)',
- ],
- valid: [
- '-$10,123.4',
- '$10,123.454',
- '$10123.452',
- '10,123.453',
- '10123.450',
- '10,123',
- '1,123,456',
- '1123456',
- '1.3',
- '.030',
- '0.100',
- '$0.1',
- '-$0.0',
- '-$.9',
- '$100,234,567.893',
- '$10,123',
- '10,123.123',
- '-10123.1',
- ],
- invalid: [
- '1.23',
- '$1.13322',
- '$ 32.50',
- '500$',
- '.0001',
- '$.01',
- '$0.01',
- '12,34.56',
- '123456,123,123456',
- '123,4',
- ',123',
- '$-,123',
- '$',
- '.',
- ',',
- '00',
- '$-',
- '$-,.',
- '-',
- '-$',
- '',
- '- $',
- ],
- });
-
- test({
- validator: 'isCurrency',
- args: [
- {
- require_symbol: true,
- },
- '-$##,###.## with $ required (en-US, en-CA, en-AU, en-NZ, en-HK)',
- ],
- valid: [
- '-$10,123.45',
- '$10,123.45',
- '$10123.45',
- '$10,123.45',
- '$10,123',
- '$1,123,456',
- '$1123456',
- '$1.39',
- '$.03',
- '$0.10',
- '$0.10',
- '-$0.01',
- '-$.99',
- '$100,234,567.89',
- '$10,123',
- '-$10123',
- ],
- invalid: [
- '1.234',
- '$1.234',
- '1.1',
- '$1.1',
- '$ 32.50',
- ' 32.50',
- '500',
- '10,123,456',
- '.0001',
- '$.001',
- '$0.001',
- '1,234.56',
- '123456,123,123456',
- '$123456,123,123456',
- '123.4',
- '$123.4',
- ',123',
- '$,123',
- '$-,123',
- '$',
- '.',
- '$.',
- ',',
- '$,',
- '00',
- '$00',
- '$-',
- '$-,.',
- '-',
- '-$',
- '',
- '$ ',
- '- $',
- ],
- });
-
- test({
- validator: 'isCurrency',
- args: [
- {
- symbol: '¥',
- negative_sign_before_digits: true,
- },
- '¥-##,###.## (zh-CN)',
- ],
- valid: [
- '123,456.78',
- '-123,456.78',
- '¥6,954,231',
- '¥-6,954,231',
- '¥10.03',
- '¥-10.03',
- '10.03',
- '1.39',
- '.03',
- '0.10',
- '¥-10567.01',
- '¥0.01',
- '¥1,234,567.89',
- '¥10,123',
- '¥-10,123',
- '¥-10,123.45',
- '10,123',
- '10123',
- '¥-100',
- ],
- invalid: [
- '1.234',
- '¥1.1',
- '5,00',
- '.0001',
- '¥.001',
- '¥0.001',
- '12,34.56',
- '123456,123,123456',
- '123 456',
- ',123',
- '¥-,123',
- '',
- ' ',
- '¥',
- '¥-',
- '¥-,.',
- '-',
- '- ¥',
- '-¥',
- ],
- });
-
- test({
- validator: 'isCurrency',
- args: [
- {
- symbol: '¥',
- allow_negatives: false,
- },
- '¥##,###.## with no negatives (zh-CN)',
- ],
- valid: [
- '123,456.78',
- '¥6,954,231',
- '¥10.03',
- '10.03',
- '1.39',
- '.03',
- '0.10',
- '¥0.01',
- '¥1,234,567.89',
- '¥10,123',
- '10,123',
- '10123',
- '¥100',
- ],
- invalid: [
- '1.234',
- '-123,456.78',
- '¥-6,954,231',
- '¥-10.03',
- '¥-10567.01',
- '¥1.1',
- '¥-10,123',
- '¥-10,123.45',
- '5,00',
- '¥-100',
- '.0001',
- '¥.001',
- '¥-.001',
- '¥0.001',
- '12,34.56',
- '123456,123,123456',
- '123 456',
- ',123',
- '¥-,123',
- '',
- ' ',
- '¥',
- '¥-',
- '¥-,.',
- '-',
- '- ¥',
- '-¥',
- ],
- });
-
- test({
- validator: 'isCurrency',
- args: [
- {
- symbol: 'R',
- negative_sign_before_digits: true,
- thousands_separator: ' ',
- decimal_separator: ',',
- allow_negative_sign_placeholder: true,
- },
- 'R ## ###,## and R-10 123,25 (el-ZA)',
- ],
- valid: [
- '123 456,78',
- '-10 123',
- 'R-10 123',
- 'R 6 954 231',
- 'R10,03',
- '10,03',
- '1,39',
- ',03',
- '0,10',
- 'R10567,01',
- 'R0,01',
- 'R1 234 567,89',
- 'R10 123',
- 'R 10 123',
- 'R 10123',
- 'R-10123',
- '10 123',
- '10123',
- ],
- invalid: [
- '1,234',
- 'R -10123',
- 'R- 10123',
- 'R,1',
- ',0001',
- 'R,001',
- 'R0,001',
- '12 34,56',
- '123456 123 123456',
- ' 123',
- '- 123',
- '123 ',
- '',
- ' ',
- 'R',
- 'R- .1',
- 'R-',
- '-',
- '-R 10123',
- 'R00',
- 'R -',
- '-R',
- ],
- });
-
- test({
- validator: 'isCurrency',
- args: [
- {
- symbol: '€',
- thousands_separator: '.',
- decimal_separator: ',',
- allow_space_after_symbol: true,
- },
- '-€ ##.###,## (it-IT)',
- ],
- valid: [
- '123.456,78',
- '-123.456,78',
- '€6.954.231',
- '-€6.954.231',
- '€ 896.954.231',
- '-€ 896.954.231',
- '16.954.231',
- '-16.954.231',
- '€10,03',
- '-€10,03',
- '10,03',
- '-10,03',
- '-1,39',
- ',03',
- '0,10',
- '-€10567,01',
- '-€ 10567,01',
- '€ 0,01',
- '€1.234.567,89',
- '€10.123',
- '10.123',
- '-€10.123',
- '€ 10.123',
- '€10.123',
- '€ 10123',
- '10.123',
- '-10123',
- ],
- invalid: [
- '1,234',
- '€ 1,1',
- '50#,50',
- '123,@€ ',
- '€€500',
- ',0001',
- '€ ,001',
- '€0,001',
- '12.34,56',
- '123456.123.123456',
- '€123€',
- '',
- ' ',
- '€',
- ' €',
- '€ ',
- '€€',
- ' 123',
- '- 123',
- '.123',
- '-€.123',
- '123 ',
- '€-',
- '- €',
- '€ - ',
- '-',
- '- ',
- '-€',
- ],
- });
-
- test({
- validator: 'isCurrency',
- args: [
- {
- symbol: '€',
- thousands_separator: '.',
- symbol_after_digits: true,
- decimal_separator: ',',
- allow_space_after_digits: true,
- },
- '-##.###,## € (el-GR)',
- ],
- valid: [
- '123.456,78',
- '-123.456,78',
- '6.954.231 €',
- '-6.954.231 €',
- '896.954.231',
- '-896.954.231',
- '16.954.231',
- '-16.954.231',
- '10,03€',
- '-10,03€',
- '10,03',
- '-10,03',
- '1,39',
- ',03',
- '-,03',
- '-,03 €',
- '-,03€',
- '0,10',
- '10567,01€',
- '0,01 €',
- '1.234.567,89€',
- '10.123€',
- '10.123',
- '10.123€',
- '10.123 €',
- '10123 €',
- '10.123',
- '10123',
- ],
- invalid: [
- '1,234',
- '1,1 €',
- ',0001',
- ',001 €',
- '0,001€',
- '12.34,56',
- '123456.123.123456',
- '€123€',
- '',
- ' ',
- '€',
- ' €',
- '€ ',
- ' 123',
- '- 123',
- '.123',
- '-.123€',
- '-.123 €',
- '123 ',
- '-€',
- '- €',
- '-',
- '- ',
- ],
- });
-
- test({
- validator: 'isCurrency',
- args: [
- {
- symbol: 'kr.',
- negative_sign_before_digits: true,
- thousands_separator: '.',
- decimal_separator: ',',
- allow_space_after_symbol: true,
- },
- 'kr. -##.###,## (da-DK)',
- ],
- valid: [
- '123.456,78',
- '-10.123',
- 'kr. -10.123',
- 'kr.-10.123',
- 'kr. 6.954.231',
- 'kr.10,03',
- 'kr. -10,03',
- '10,03',
- '1,39',
- ',03',
- '0,10',
- 'kr. 10567,01',
- 'kr. 0,01',
- 'kr. 1.234.567,89',
- 'kr. -1.234.567,89',
- '10.123',
- 'kr. 10.123',
- 'kr.10.123',
- '10123',
- '10.123',
- 'kr.-10123',
- ],
- invalid: [
- '1,234',
- 'kr. -10123',
- 'kr.,1',
- ',0001',
- 'kr. ,001',
- 'kr.0,001',
- '12.34,56',
- '123456.123.123456',
- '.123',
- 'kr.-.123',
- 'kr. -.123',
- '- 123',
- '123 ',
- '',
- ' ',
- 'kr.',
- ' kr.',
- 'kr. ',
- 'kr.-',
- 'kr. -',
- 'kr. - ',
- ' - ',
- '-',
- '- kr.',
- '-kr.',
- ],
- });
-
- test({
- validator: 'isCurrency',
- args: [
- {
- symbol: 'kr.',
- allow_negatives: false,
- negative_sign_before_digits: true,
- thousands_separator: '.',
- decimal_separator: ',',
- allow_space_after_symbol: true,
- },
- 'kr. ##.###,## with no negatives (da-DK)',
- ],
- valid: [
- '123.456,78',
- '10.123',
- 'kr. 10.123',
- 'kr.10.123',
- 'kr. 6.954.231',
- 'kr.10,03',
- 'kr. 10,03',
- '10,03',
- '1,39',
- ',03',
- '0,10',
- 'kr. 10567,01',
- 'kr. 0,01',
- 'kr. 1.234.567,89',
- 'kr.1.234.567,89',
- '10.123',
- 'kr. 10.123',
- 'kr.10.123',
- '10123',
- '10.123',
- 'kr.10123',
- ],
- invalid: [
- '1,234',
- '-10.123',
- 'kr. -10.123',
- 'kr. -1.234.567,89',
- 'kr.-10123',
- 'kr. -10123',
- 'kr.-10.123',
- 'kr. -10,03',
- 'kr.,1',
- ',0001',
- 'kr. ,001',
- 'kr.0,001',
- '12.34,56',
- '123456.123.123456',
- '.123',
- 'kr.-.123',
- 'kr. -.123',
- '- 123',
- '123 ',
- '',
- ' ',
- 'kr.',
- ' kr.',
- 'kr. ',
- 'kr.-',
- 'kr. -',
- 'kr. - ',
- ' - ',
- '-',
- '- kr.',
- '-kr.',
- ],
- });
-
- test({
- validator: 'isCurrency',
- args: [
- {
- parens_for_negatives: true,
- },
- '($##,###.##) (en-US, en-HK)',
- ],
- valid: [
- '1,234',
- '(1,234)',
- '($6,954,231)',
- '$10.03',
- '(10.03)',
- '($10.03)',
- '1.39',
- '.03',
- '(.03)',
- '($.03)',
- '0.10',
- '$10567.01',
- '($0.01)',
- '$1,234,567.89',
- '$10,123',
- '(10,123)',
- '10123',
- ],
- invalid: [
- '1.234',
- '($1.1)',
- '-$1.10',
- '$ 32.50',
- '500$',
- '.0001',
- '$.001',
- '($0.001)',
- '12,34.56',
- '123456,123,123456',
- '( 123)',
- ',123',
- '$-,123',
- '',
- ' ',
- ' ',
- ' ',
- '$',
- '$ ',
- ' $',
- ' 123',
- '(123) ',
- '.',
- ',',
- '00',
- '$-',
- '$ - ',
- '$- ',
- ' - ',
- '-',
- '- $',
- '-$',
- '()',
- '( )',
- '( -)',
- '( - )',
- '( - )',
- '(-)',
- '(-$)',
- ],
- });
-
- test({
- validator: 'isCurrency',
- args: [
- { allow_negatives: false },
- '$##,###.## with no negatives (en-US, en-CA, en-AU, en-HK)',
- ],
- valid: [
- '$10,123.45',
- '$10123.45',
- '10,123.45',
- '10123.45',
- '10,123',
- '1,123,456',
- '1123456',
- '1.39',
- '.03',
- '0.10',
- '$0.10',
- '$100,234,567.89',
- '$10,123',
- '10,123',
- ],
- invalid: [
- '1.234',
- '-1.234',
- '-10123',
- '-$0.01',
- '-$.99',
- '$1.1',
- '-$1.1',
- '$ 32.50',
- '500$',
- '.0001',
- '$.001',
- '$0.001',
- '12,34.56',
- '123456,123,123456',
- '-123456,123,123456',
- '123,4',
- ',123',
- '$-,123',
- '$',
- '.',
- ',',
- '00',
- '$-',
- '$-,.',
- '-',
- '-$',
- '',
- '- $',
- '-$10,123.45',
- ],
- });
-
- test({
- validator: 'isBoolean',
- valid: [
- 'true',
- 'false',
- '0',
- '1',
- ],
- invalid: [
- '1.0',
- '0.0',
- 'true ',
- 'False',
- 'True',
- 'yes',
- ],
- });
- });
-
- it('should validate ISO 8601 dates', function () {
- // from http://www.pelagodesign.com/blog/2009/05/20/iso-8601-date-validation-that-doesnt-suck/
- test({
- validator: 'isISO8601',
- valid: [
- '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',
- ],
- invalid: [
- '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',
- ],
- });
- });
-
- it('should validate ISO 3166-1 alpha 2 country codes', function () {
- // from https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2
- test({
- validator: 'isISO31661Alpha2',
- valid: [
- 'FR',
- 'fR',
- 'GB',
- 'PT',
- 'CM',
- 'JP',
- 'PM',
- 'ZW',
- 'MM',
- 'cc',
- 'GG',
- ],
- invalid: [
- '',
- 'FRA',
- 'AA',
- 'PI',
- 'RP',
- 'WV',
- 'WL',
- 'UK',
- 'ZZ',
- ],
- });
- });
-
- it('should validate whitelisted characters', function () {
- test({
- validator: 'isWhitelisted',
- args: ['abcdefghijklmnopqrstuvwxyz-'],
- valid: ['foo', 'foobar', 'baz-foo'],
- invalid: ['foo bar', 'fo.bar', 'türkçe'],
- });
- });
-
- it('should error on non-string input', function () {
- var empty = [undefined, null, [], NaN];
- empty.forEach(function (item) {
- assert.throws(validator.isEmpty.bind(null, item));
- });
- });
-
- it('should validate dataURI', function () {
- /* eslint-disable max-len */
- test({
- validator: 'isDataURI',
- valid: [
- 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD///+l2Z/dAAAAM0lEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4Ug9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC',
- 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIBAMAAAA2IaO4AAAAFVBMVEXk5OTn5+ft7e319fX29vb5+fn///++GUmVAAAALUlEQVQIHWNICnYLZnALTgpmMGYIFWYIZTA2ZFAzTTFlSDFVMwVyQhmAwsYMAKDaBy0axX/iAAAAAElFTkSuQmCC',
- ' data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIBAMAAAA2IaO4AAAAFVBMVEXk5OTn5+ft7e319fX29vb5+fn///++GUmVAAAALUlEQVQIHWNICnYLZnALTgpmMGYIFWYIZTA2ZFAzTTFlSDFVMwVyQhmAwsYMAKDaBy0axX/iAAAAAElFTkSuQmCC ',
- 'data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22100%22%20height%3D%22100%22%3E%3Crect%20fill%3D%22%2300B1FF%22%20width%3D%22100%22%20height%3D%22100%22%2F%3E%3C%2Fsvg%3E',
- 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDAiIGhlaWdodD0iMTAwIj48cmVjdCBmaWxsPSIjMDBCMUZGIiB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCIvPjwvc3ZnPg==',
- ' data:,Hello%2C%20World!',
- ' data:,Hello World!',
- ' data:text/plain;base64,SGVsbG8sIFdvcmxkIQ%3D%3D',
- ' data:text/html,%3Ch1%3EHello%2C%20World!%3C%2Fh1%3E',
- 'data:,A%20brief%20note',
- 'data:text/html;charset=US-ASCII,%3Ch1%3EHello!%3C%2Fh1%3E',
- ],
- invalid: [
- 'dataxbase64',
- 'data:HelloWorld',
- 'data:text/html;charset=,%3Ch1%3EHello!%3C%2Fh1%3E',
- 'data:text/html;charset,%3Ch1%3EHello!%3C%2Fh1%3E', 'data:base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD///+l2Z/dAAAAM0lEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4Ug9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC',
- '',
- 'http://wikipedia.org',
- 'base64',
- 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD///+l2Z/dAAAAM0lEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4Ug9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC',
- ],
- });
- /* eslint-enable max-len */
- });
-
- it('should validate LatLong', function () {
- test({
- validator: 'isLatLong',
- valid: [
- '(-17.738223, 85.605469)',
- '(-12.3456789, +12.3456789)',
- '(-60.978437, -0.175781)',
- '(77.719772, -37.529297)',
- '(7.264394, 165.058594)',
- '0.955766, -19.863281',
- '(31.269161,164.355469)',
- '+12.3456789, -12.3456789',
- '-15.379543, -137.285156',
- '(11.770570, -162.949219)',
- '-55.034319, 113.027344',
- '58.025555, 36.738281',
- '55.720923,-28.652344',
- '-90.00000,-180.00000',
- '(-71, -146)',
- '(-71.616864, -146.616864)',
- '-0.55, +0.22',
- '90, 180',
- '+90, -180',
- '-90,+180',
- '90,180',
- '0, 0',
- ],
- invalid: [
- '(020.000000, 010.000000000)',
- '89.9999999989, 360.0000000',
- '90.1000000, 180.000000',
- '+90.000000, -180.00001',
- '090.0000, 0180.0000',
- '126, -158',
- '(-126.400010, -158.400010)',
- '-95, -96',
- '-95.738043, -96.738043',
- '137, -148',
- '(-137.5942, -148.5942)',
- '(-120, -203)',
- '(-119, -196)',
- '+119.821728, -196.821728',
- '(-110, -223)',
- '-110.369532, 223.369532',
- '(-120.969949, +203.969949)',
- '-116, -126',
- '-116.894222, -126.894222',
- '-112, -160',
- '-112.96381, -160.96381',
- '-90., -180.',
- '+90.1, -180.1',
- '+,-',
- '(,)',
- ',',
- ' ',
- ],
- });
- });
-
- it('should validate postal code', function () {
- const fixtures = [
- {
- locale: 'AU',
- valid: [
- '4000',
- '2620',
- '3000',
- '2017',
- '0800',
- ],
- },
- {
- locale: 'CA',
- valid: [
- 'L4T 0A5',
- 'G1A-0A2',
- 'A1A 1A1',
- 'X0A-0H0',
- 'V5K 0A1',
- ],
- },
- {
- locale: 'JP',
- valid: [
- '135-0000',
- '874-8577',
- '669-1161',
- '470-0156',
- '672-8031',
- ],
- },
- {
- locale: 'GR',
- valid: [
- '022 93',
- '29934',
- '90293',
- '299 42',
- '94944',
- ],
- },
- {
- locale: 'GB',
- valid: [
- 'TW8 9GS',
- 'BS98 1TL',
- 'DE99 3GG',
- 'DE55 4SW',
- 'DH98 1BT',
- 'DH99 1NS',
- 'GIR0aa',
- 'SA99',
- 'W1N 4DJ',
- 'AA9A 9AA',
- 'AA99 9AA',
- 'BS98 1TL',
- 'DE993GG',
- ],
- },
- {
- locale: 'FR',
- valid: [
- '75008',
- '44 522',
- '98025',
- '38 499',
- '39940',
- ],
- },
- {
- locale: 'CZ',
- valid: [
- '20134',
- '392 90',
- '39919',
- '938 29',
- '39949',
- ],
- },
- {
- locale: 'NL',
- valid: [
- '1012 SZ',
- '3432FE',
- '1118 BH',
- '3950IO',
- '3997 GH',
- ],
- },
- {
- locale: 'PL',
- valid: [
- '47-260',
- '12-930',
- '78-399',
- '39-490',
- '38-483',
- ],
- },
- {
- locale: 'TW',
- valid: [
- '360',
- '90312',
- '399',
- '935',
- '38842',
- ],
- },
- {
- locale: 'LI',
- valid: [
- '9485',
- '9497',
- '9491',
- '9489',
- '9496',
- ],
- },
- {
- locale: 'PT',
- valid: [
- '4827',
- '4829-489',
- '0294-348',
- '1928',
- '8156-392',
- ],
- },
- {
- locale: 'SE',
- valid: [
- '12994',
- '284 39',
- '39556',
- '489 39',
- '499 49',
- ],
- },
- ];
-
- let allValid = [];
-
- // Test fixtures
- fixtures.forEach(function (fixture) {
- if (fixture.valid) allValid = allValid.concat(fixture.valid);
- test({
- validator: 'isPostalCode',
- valid: fixture.valid,
- invalid: fixture.invalid,
- args: [fixture.locale],
- });
- });
-
- // Test generics
- test({
- validator: 'isPostalCode',
- valid: [
- ...allValid,
- '1234',
- '6900',
- '1292',
- '9400',
- '27616',
- '90210',
- '10001',
- '21201',
- '33142',
- '060623',
- '123456',
- '293940',
- '002920',
- ],
- invalid: [
- 'asdf',
- '1',
- 'ASDFGJKLmZXJtZtesting123',
- 'Vml2YW11cyBmZXJtZtesting123',
- '48380480343',
- '29923-329393-2324',
- '4294924224',
- '13',
- ],
- args: ['any'],
- });
- });
-});
diff --git a/test/validators.test.js b/test/validators.test.js
new file mode 100644
index 000000000..c5ea4dc99
--- /dev/null
+++ b/test/validators.test.js
@@ -0,0 +1,15969 @@
+import assert from 'assert';
+import fs from 'fs';
+import timezone_mock from 'timezone-mock';
+import vm from 'vm';
+import test from './testFunctions';
+
+let validator_js = fs.readFileSync(require.resolve('../validator.js')).toString();
+
+describe('Validators', () => {
+ it('should validate email addresses', () => {
+ test({
+ validator: 'isEmail',
+ valid: [
+ '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',
+ 'test123+ext@gmail.com',
+ 'some.name.midd.leNa.me.and.locality+extension@GoogleMail.com',
+ '"foobar"@example.com',
+ '" foo m端ller "@example.com',
+ '"foo\\@bar"@example.com',
+ `${'a'.repeat(64)}@${'a'.repeat(63)}.com`,
+ `${'a'.repeat(64)}@${'a'.repeat(63)}.com`,
+ `${'a'.repeat(31)}@gmail.com`,
+ 'test@gmail.com',
+ 'test.1@gmail.com',
+ 'test@1337.com',
+ ],
+ invalid: [
+ 'invalidemail@',
+ 'invalid.com',
+ '@invalid.com',
+ 'foo@bar.com.',
+ 'foo@_bar.com',
+ 'somename@gmail.com',
+ 'foo@bar.co.uk.',
+ 'z@co.c',
+ 'gmailgmailgmailgmailgmail@gmail.com',
+ `${'a'.repeat(64)}@${'a'.repeat(251)}.com`,
+ `${'a'.repeat(65)}@${'a'.repeat(250)}.com`,
+ `${'a'.repeat(64)}@${'a'.repeat(64)}.com`,
+ `${'a'.repeat(64)}@${'a'.repeat(63)}.${'a'.repeat(63)}.${'a'.repeat(63)}.${'a'.repeat(58)}.com`,
+ 'test1@invalid.co m',
+ 'test2@invalid.co m',
+ 'test3@invalid.co m',
+ 'test4@invalid.co m',
+ 'test5@invalid.co m',
+ 'test6@invalid.co m',
+ 'test7@invalid.co m',
+ 'test8@invalid.co m',
+ 'test9@invalid.co m',
+ 'test10@invalid.co m',
+ 'test11@invalid.co m',
+ 'test12@invalid.co m',
+ 'test13@invalid.co m',
+ 'multiple..dots@stillinvalid.com',
+ 'test123+invalid! sub_address@gmail.com',
+ 'gmail...ignores...dots...@gmail.com',
+ 'ends.with.dot.@gmail.com',
+ 'multiple..dots@gmail.com',
+ 'wrong()[]",:;<>@@gmail.com',
+ '"wrong()[]",:;<>@@gmail.com',
+ 'username@domain.com�',
+ 'username@domain.com©',
+ 'nbsp test@test.com',
+ 'nbsp_test@te st.com',
+ 'nbsp_test@test.co m',
+ '"foobar@gmail.com',
+ '"foo"bar@gmail.com',
+ 'foo"bar"@gmail.com',
+ ],
+ });
+ });
+
+ it('should validate email addresses with domain specific validation', () => {
+ test({
+ validator: 'isEmail',
+ args: [{ domain_specific_validation: true }],
+ valid: [
+ 'foobar@gmail.com',
+ 'foo.bar@gmail.com',
+ 'foo.bar@googlemail.com',
+ `${'a'.repeat(30)}@gmail.com`,
+ ],
+ invalid: [
+ `${'a'.repeat(31)}@gmail.com`,
+ 'test@gmail.com',
+ 'test.1@gmail.com',
+ '.foobar@gmail.com',
+ ],
+ });
+ });
+
+ it('should validate email addresses with underscores in the domain', () => {
+ test({
+ validator: 'isEmail',
+ args: [{ allow_underscores: true }],
+ valid: [
+ 'foobar@my_sarisari_store.typepad.com',
+ ],
+ invalid: [],
+ });
+ });
+
+ it('should validate email addresses without UTF8 characters in local part', () => {
+ test({
+ validator: 'isEmail',
+ args: [{ allow_utf8_local_part: false }],
+ valid: [
+ 'foo@bar.com',
+ 'x@x.au',
+ 'foo@bar.com.au',
+ 'foo+bar@bar.com',
+ 'hans@m端ller.com',
+ 'test|123@m端ller.com',
+ 'test123+ext@gmail.com',
+ 'some.name.midd.leNa.me+extension@GoogleMail.com',
+ '"foobar"@example.com',
+ '"foo\\@bar"@example.com',
+ '" foo bar "@example.com',
+ ],
+ invalid: [
+ 'invalidemail@',
+ 'invalid.com',
+ '@invalid.com',
+ 'foo@bar.com.',
+ 'foo@bar.co.uk.',
+ 'somename@gmail.com',
+ 'hans.m端ller@test.com',
+ 'z@co.c',
+ 'tüst@invalid.com',
+ 'nbsp test@test.com',
+ ],
+ });
+ });
+
+ it('should validate email addresses with display names', () => {
+ test({
+ validator: 'isEmail',
+ args: [{ allow_display_name: true }],
+ valid: [
+ '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',
+ 'test123+ext@gmail.com',
+ 'some.name.midd.leNa.me+extension@GoogleMail.com',
+ 'Some Name ',
+ 'Some Name ',
+ 'Some Name ',
+ 'Some Name ',
+ 'Some Name ',
+ 'Some Name ',
+ 'Some Name ',
+ 'Some Name ',
+ '\'Foo Bar, Esq\'',
+ 'Some Name ',
+ 'Some Middle Name ',
+ 'Name ',
+ 'Name',
+ 'Some Name ',
+ 'Name🍓With🍑Emoji🚴♀️🏆',
+ '🍇🍗🍑',
+ '""',
+ '"\\"quotes\\""',
+ '"name;"',
+ '"name;" ',
+ ],
+ invalid: [
+ 'invalidemail@',
+ 'invalid.com',
+ '@invalid.com',
+ 'foo@bar.com.',
+ 'foo@bar.co.uk.',
+ 'Some Name ',
+ 'Some Name ',
+ 'Some Name <@invalid.com>',
+ 'Some Name ',
+ 'Some Name ',
+ 'Some Name foo@bar.co.uk.>',
+ 'Some Name ',
+ 'Name foo@bar.co.uk',
+ 'Some Name ',
+ 'Some Name',
+ 'invisibleCharacter\u001F',
+ '',
+ '\\"quotes\\"',
+ '""quotes""',
+ 'name;',
+ ' ',
+ '" "',
+ ],
+ });
+ });
+
+ it('should validate email addresses with required display names', () => {
+ test({
+ validator: 'isEmail',
+ args: [{ require_display_name: true }],
+ valid: [
+ 'Some Name ',
+ 'Some Name ',
+ 'Some Name ',
+ 'Some Name ',
+ 'Some Name ',
+ 'Some Name ',
+ 'Some Name ',
+ 'Some Name ',
+ 'Some Name ',
+ 'Some Middle Name ',
+ 'Name ',
+ 'Name',
+ ],
+ invalid: [
+ 'some.name.midd.leNa.me+extension@GoogleMail.com',
+ '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',
+ 'test123+ext@gmail.com',
+ 'invalidemail@',
+ 'invalid.com',
+ '@invalid.com',
+ 'foo@bar.com.',
+ 'foo@bar.co.uk.',
+ 'Some Name ',
+ 'Some Name ',
+ 'Some Name <@invalid.com>',
+ 'Some Name ',
+ 'Some Name ',
+ 'Some Name foo@bar.co.uk.>',
+ 'Some Name ',
+ 'Name foo@bar.co.uk',
+ ],
+ });
+ });
+
+ it('should validate email addresses with allowed IPs', () => {
+ test({
+ validator: 'isEmail',
+ args: [{ allow_ip_domain: true }],
+ valid: [
+ 'email@[123.123.123.123]',
+ 'email@255.255.255.255',
+ ],
+ invalid: [
+ 'email@0.0.0.256',
+ 'email@26.0.0.256',
+ 'email@[266.266.266.266]',
+ ],
+ });
+ });
+
+ it('should not validate email addresses with blacklisted chars in the name', () => {
+ test({
+ validator: 'isEmail',
+ args: [{ blacklisted_chars: 'abc"' }],
+ valid: [
+ 'emil@gmail.com',
+ ],
+ invalid: [
+ 'email@gmail.com',
+ '"foobr"@example.com',
+ '" foo m端ller "@example.com',
+ '"foo\@br"@example.com',
+ ],
+ });
+ });
+
+
+ it('should validate really long emails if ignore_max_length is set', () => {
+ test({
+ validator: 'isEmail',
+ args: [{ ignore_max_length: false }],
+ valid: [],
+ invalid: [
+ 'Deleted-user-id-19430-Team-5051deleted-user-id-19430-team-5051XXXXXX@example.com',
+ ],
+ });
+
+ test({
+ validator: 'isEmail',
+ args: [{ ignore_max_length: true }],
+ valid: [
+ 'Deleted-user-id-19430-Team-5051deleted-user-id-19430-team-5051XXXXXX@example.com',
+ ],
+ invalid: [],
+ });
+
+ test({
+ validator: 'isEmail',
+ args: [{ ignore_max_length: true }],
+ valid: [
+ 'Deleted-user-id-19430-Team-5051deleted-user-id-19430-team-5051XXXXXX@Deleted-user-id-19430-Team-5051deleted-user-id-19430-team-5051XXXXXX.com',
+ ],
+ invalid: [],
+ });
+ });
+
+ it('should not validate email addresses with denylisted domains', () => {
+ test({
+ validator: 'isEmail',
+ args: [{ host_blacklist: ['gmail.com', 'foo.bar.com'] }],
+ valid: [
+ 'email@foo.gmail.com',
+ ],
+ invalid: [
+ 'foo+bar@gmail.com',
+ 'email@foo.bar.com',
+ ],
+ });
+ });
+
+ it('should allow regular expressions in the host blacklist of isEmail', () => {
+ test({
+ validator: 'isEmail',
+ args: [{
+ host_blacklist: ['bar.com', 'foo.com', /\.foo\.com$/],
+ }],
+ valid: [
+ 'email@foobar.com',
+ 'email@foo.bar.com',
+ 'email@qux.com',
+ ],
+ invalid: [
+ 'email@bar.com',
+ 'email@foo.com',
+ 'email@a.b.c.foo.com',
+ ],
+ });
+ });
+
+ it('should validate only email addresses with whitelisted domains', () => {
+ test({
+ validator: 'isEmail',
+ args: [{ host_whitelist: ['gmail.com', 'foo.bar.com'] }],
+ valid: [
+ 'email@gmail.com',
+ 'test@foo.bar.com',
+ ],
+ invalid: [
+ 'foo+bar@test.com',
+ 'email@foo.com',
+ 'email@bar.com',
+ ],
+ });
+ });
+
+ it('should allow regular expressions in the host whitelist of isEmail', () => {
+ test({
+ validator: 'isEmail',
+ args: [{
+ host_whitelist: ['bar.com', 'foo.com', /\.foo\.com$/],
+ }],
+ valid: [
+ 'email@bar.com',
+ 'email@foo.com',
+ 'email@a.b.c.foo.com',
+ ],
+ invalid: [
+ 'email@foobar.com',
+ 'email@foo.bar.com',
+ 'email@qux.com',
+ ],
+ });
+ });
+
+ it('should validate URLs', () => {
+ test({
+ validator: 'isURL',
+ valid: [
+ 'foobar.com',
+ 'www.foobar.com',
+ 'foobar.com/',
+ 'valid.au',
+ 'http://www.foobar.com/',
+ 'HTTP://WWW.FOOBAR.COM/',
+ 'https://www.foobar.com/',
+ 'HTTPS://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://: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',
+ 'http://xn------eddceddeftq7bvv7c4ke4c.xn--p1ai',
+ 'http://кулік.укр',
+ 'test.com?ref=http://test2.com',
+ 'http://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:80/index.html',
+ 'http://[1080:0:0:0:8:800:200C:417A]/index.html',
+ 'http://[3ffe:2a00:100:7031::1]',
+ 'http://[1080::8:800:200C:417A]/foo',
+ 'http://[::192.9.5.5]/ipng',
+ 'http://[::FFFF:129.144.52.38]:80/index.html',
+ 'http://[2010:836B:4179::836B:4179]',
+ 'http://example.com/example.json#/foo/bar',
+ 'http://1337.com',
+ // TODO: those probably should not be marked as valid URLs; CVE-2025-56200
+ /* eslint-disable no-script-url */
+ 'javascript:%61%6c%65%72%74%28%31%29@example.com',
+ 'http://evil-site.com@example.com/',
+ 'javascript:alert(1)@example.com',
+ /* eslint-enable no-script-url */
+ ],
+ invalid: [
+ 'http://localhost:3000/',
+ '//foobar.com',
+ '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://@foobar.com',
+ 'http://:@foobar.com',
+ 'http://\n@www.foobar.com/',
+ '',
+ `http://foobar.com/${new Array(2083).join('f')}`,
+ 'http://*.foo.com',
+ '*.foo.com',
+ '!.foo.com',
+ 'http://example.com.',
+ 'http://localhost:61500this is an invalid url!!!!',
+ '////foobar.com',
+ 'http:////foobar.com',
+ 'https://example.com/foo//',
+ // the following tests are because of CVE-2025-56200
+ /* eslint-disable no-script-url */
+ "javascript:alert(1);a=';@example.com/alert(1)'",
+ 'JaVaScRiPt:alert(1)@example.com',
+ 'javascript:/* comment */alert(1)@example.com',
+ 'javascript:var a=1; alert(a);@example.com',
+ 'javascript:alert(1)@user@example.com',
+ 'javascript:alert(1)@example.com?q=safe',
+ 'data:text/html,@example.com',
+ 'vbscript:msgbox("XSS")@example.com',
+ '//evil-site.com/path@example.com',
+ /* eslint-enable no-script-url */
+ ],
+ });
+ });
+
+ it('should validate URLs without protocol', () => {
+ test({
+ validator: 'isURL',
+ args: [{
+ require_tld: false,
+ require_valid_protocol: false,
+ }],
+ valid: [
+ 'localhost',
+ 'localhost:3000',
+ 'service-name:8080',
+ 'https://localhost',
+ 'http://localhost:3000',
+ 'http://service-name:8080',
+ 'user:password@localhost',
+ 'user:pass@service-name:8080',
+ ],
+ invalid: [],
+ });
+
+ // Test with require_protocol: true - should reject hostnames with ports but no protocol
+ test({
+ validator: 'isURL',
+ args: [{
+ require_tld: false,
+ require_protocol: true,
+ require_valid_protocol: false,
+ }],
+ valid: [
+ 'http://localhost:3000',
+ 'https://service-name:8080',
+ 'custom://localhost',
+ ],
+ invalid: [
+ 'localhost:3000',
+ 'service-name:8080',
+ 'user:password@localhost',
+ ],
+ });
+
+ // Test non-numeric patterns after colon (should be treated as protocols)
+ test({
+ validator: 'isURL',
+ args: [{
+ require_tld: false,
+ require_valid_protocol: false,
+ protocols: ['custom', 'myscheme'],
+ }],
+ valid: [
+ 'custom:something',
+ 'myscheme:data',
+ ],
+ invalid: [],
+ });
+ });
+
+ it('should validate URLs with custom protocols', () => {
+ test({
+ validator: 'isURL',
+ args: [{
+ protocols: ['rtmp'],
+ }],
+ valid: [
+ 'rtmp://foobar.com',
+ 'rtmp:foobar.com',
+ ],
+ invalid: [
+ 'http://foobar.com',
+ 'tel:+15551234567',
+ ],
+ });
+ });
+
+ it('should validate file URLs without a host', () => {
+ test({
+ validator: 'isURL',
+ args: [{
+ protocols: ['file'],
+ require_host: false,
+ require_tld: false,
+ }],
+ valid: [
+ 'file://localhost/foo.txt',
+ 'file:///foo.txt',
+ 'file:///',
+ ],
+ invalid: [
+ 'http://foobar.com',
+ 'file://',
+ ],
+ });
+ });
+
+ it('should validate postgres URLs without a host', () => {
+ test({
+ validator: 'isURL',
+ args: [{
+ protocols: ['postgres'],
+ require_host: false,
+ }],
+ valid: [
+ 'postgres://user:pw@/test',
+ ],
+ invalid: [
+ 'http://foobar.com',
+ 'postgres://',
+ ],
+ });
+ });
+
+
+ it('should validate URLs with any protocol', () => {
+ test({
+ validator: 'isURL',
+ args: [{
+ require_valid_protocol: false,
+ }],
+ valid: [
+ 'rtmp://foobar.com',
+ 'http://foobar.com',
+ 'test://foobar.com',
+ // Dangerous! This allows to mark malicious URLs as a valid URL (CVE-2025-56200)
+ // eslint-disable-next-line no-script-url
+ 'javascript:alert(1);@example.com',
+ ],
+ invalid: [
+ 'mailto:test@example.com',
+ ],
+ });
+ });
+
+ it('should validate URLs with underscores', () => {
+ test({
+ validator: 'isURL',
+ args: [{
+ allow_underscores: true,
+ }],
+ valid: [
+ 'http://foo_bar.com',
+ 'http://pr.example_com.294.example.com/',
+ 'http://foo__bar.com',
+ 'http://_.example.com',
+ ],
+ invalid: [],
+ });
+ });
+
+ it('should validate URLs that do not have a TLD', () => {
+ test({
+ validator: 'isURL',
+ args: [{
+ require_tld: false,
+ }],
+ valid: [
+ 'http://foobar.com/',
+ 'http://foobar/',
+ 'http://localhost/',
+ 'foobar/',
+ 'foobar',
+ ],
+ invalid: [],
+ });
+ });
+
+ it('should validate URLs with a trailing dot option', () => {
+ test({
+ validator: 'isURL',
+ args: [{
+ allow_trailing_dot: true,
+ require_tld: false,
+ }],
+ valid: [
+ 'http://example.com.',
+ 'foobar.',
+ ],
+ });
+ });
+
+ it('should validate URLs with column and no port', () => {
+ test({
+ validator: 'isURL',
+ valid: [
+ 'http://example.com:',
+ 'ftp://example.com:',
+ ],
+ invalid: [
+ 'https://example.com:abc',
+ ],
+ });
+ });
+
+ it('should validate sftp protocol URL containing column and no port', () => {
+ test({
+ validator: 'isURL',
+ args: [{
+ protocols: ['sftp'],
+ }],
+ valid: [
+ 'sftp://user:pass@terminal.aws.test.nl:/incoming/things.csv',
+ ],
+ });
+ });
+
+ it('should validate protocol relative URLs', () => {
+ test({
+ validator: 'isURL',
+ args: [{
+ allow_protocol_relative_urls: true,
+ }],
+ valid: [
+ '//foobar.com',
+ 'http://foobar.com',
+ 'foobar.com',
+ ],
+ invalid: [
+ '://foobar.com',
+ '/foobar.com',
+ '////foobar.com',
+ 'http:////foobar.com',
+ ],
+ });
+ });
+
+ it('should not validate URLs with fragments when allow fragments is false', () => {
+ test({
+ validator: 'isURL',
+ args: [{
+ allow_fragments: false,
+ }],
+ valid: [
+ 'http://foobar.com',
+ 'foobar.com',
+ ],
+ invalid: [
+ 'http://foobar.com#part',
+ 'foobar.com#part',
+ ],
+ });
+ });
+
+ it('should not validate URLs with query components when allow query components is false', () => {
+ test({
+ validator: 'isURL',
+ args: [{
+ allow_query_components: false,
+ }],
+ valid: [
+ 'http://foobar.com',
+ 'foobar.com',
+ ],
+ invalid: [
+ 'http://foobar.com?foo=bar',
+ 'http://foobar.com?foo=bar&bar=foo',
+ 'foobar.com?foo=bar',
+ 'foobar.com?foo=bar&bar=foo',
+ ],
+ });
+ });
+
+ it('should not validate protocol relative URLs when require protocol is true', () => {
+ test({
+ validator: 'isURL',
+ args: [{
+ allow_protocol_relative_urls: true,
+ require_protocol: true,
+ }],
+ valid: [
+ 'http://foobar.com',
+ ],
+ invalid: [
+ '//foobar.com',
+ '://foobar.com',
+ '/foobar.com',
+ 'foobar.com',
+ ],
+ });
+ });
+
+ it('should let users specify whether URLs require a protocol', () => {
+ test({
+ validator: 'isURL',
+ args: [{
+ require_protocol: true,
+ }],
+ valid: [
+ 'http://foobar.com/',
+ ],
+ invalid: [
+ 'http://localhost/',
+ 'foobar.com',
+ 'foobar',
+ ],
+ });
+ });
+
+ it('should validate authentication strings if a protocol is not required', () => {
+ test({
+ validator: 'isURL',
+ args: [{
+ require_protocol: false,
+ }],
+ valid: [
+ 'user:pw@foobar.com/',
+ ],
+ invalid: [
+ 'user:pw,@foobar.com/',
+ ],
+ });
+ });
+
+ it('should reject authentication strings if a protocol is required', () => {
+ test({
+ validator: 'isURL',
+ args: [{
+ require_protocol: true,
+ }],
+ valid: [
+ 'http://user:pw@foobar.com/',
+ 'https://user:password@example.com',
+ 'ftp://admin:pass@ftp.example.com/',
+ ],
+ invalid: [
+ 'user:pw@foobar.com/',
+ 'user:password@example.com',
+ 'admin:pass@ftp.example.com/',
+ ],
+ });
+ });
+
+ it('should reject invalid protocols when require_valid_protocol is enabled', () => {
+ test({
+ validator: 'isURL',
+ args: [{
+ require_valid_protocol: true,
+ protocols: ['http', 'https', 'ftp'],
+ }],
+ valid: [
+ 'http://example.com',
+ 'https://example.com',
+ 'ftp://example.com',
+ ],
+ invalid: [
+ // eslint-disable-next-line no-script-url
+ 'javascript:alert(1);@example.com',
+ 'data:text/html,@example.com',
+ 'file:///etc/passwd@example.com',
+ ],
+ });
+ });
+
+ it('should let users specify a host whitelist', () => {
+ test({
+ validator: 'isURL',
+ args: [{
+ host_whitelist: ['foo.com', 'bar.com'],
+ }],
+ valid: [
+ 'http://bar.com/',
+ 'http://foo.com/',
+ ],
+ invalid: [
+ 'http://foobar.com',
+ 'http://foo.bar.com/',
+ 'http://qux.com',
+ ],
+ });
+ });
+
+ it('should allow regular expressions in the host whitelist', () => {
+ test({
+ validator: 'isURL',
+ args: [{
+ host_whitelist: ['bar.com', 'foo.com', /\.foo\.com$/],
+ }],
+ valid: [
+ 'http://bar.com/',
+ 'http://foo.com/',
+ 'http://images.foo.com/',
+ 'http://cdn.foo.com/',
+ 'http://a.b.c.foo.com/',
+ ],
+ invalid: [
+ 'http://foobar.com',
+ 'http://foo.bar.com/',
+ 'http://qux.com',
+ ],
+ });
+ });
+
+ it('should let users specify a host blacklist', () => {
+ test({
+ validator: 'isURL',
+ args: [{
+ host_blacklist: ['foo.com', 'bar.com'],
+ }],
+ valid: [
+ 'http://foobar.com',
+ 'http://foo.bar.com/',
+ 'http://qux.com',
+ ],
+ invalid: [
+ 'http://bar.com/',
+ 'http://foo.com/',
+ ],
+ });
+ });
+
+ it('should allow regular expressions in the host blacklist', () => {
+ test({
+ validator: 'isURL',
+ args: [{
+ host_blacklist: ['bar.com', 'foo.com', /\.foo\.com$/],
+ }],
+ valid: [
+ 'http://foobar.com',
+ 'http://foo.bar.com/',
+ 'http://qux.com',
+ ],
+ invalid: [
+ 'http://bar.com/',
+ 'http://foo.com/',
+ 'http://images.foo.com/',
+ 'http://cdn.foo.com/',
+ 'http://a.b.c.foo.com/',
+ ],
+ });
+ });
+
+ it('GHSA-9965-vmph-33xx vulnerability - protocol delimiter parsing difference', () => {
+ const DOMAIN_WHITELIST = ['example.com'];
+
+ test({
+ validator: 'isURL',
+ args: [{
+ protocols: ['https'],
+ host_whitelist: DOMAIN_WHITELIST,
+ require_host: false,
+ }],
+ valid: [],
+ invalid: [
+ // eslint-disable-next-line no-script-url
+ "javascript:alert(1);a=';@example.com/alert(1)",
+ ],
+ });
+ });
+
+ it('should allow rejecting urls containing authentication information', () => {
+ test({
+ validator: 'isURL',
+ args: [{ disallow_auth: true }],
+ valid: [
+ 'doe.com',
+ ],
+ invalid: [
+ 'john@doe.com',
+ 'john:john@doe.com',
+ ],
+ });
+ });
+
+ it('should accept urls containing authentication information', () => {
+ test({
+ validator: 'isURL',
+ args: [{ disallow_auth: false }],
+ valid: [
+ 'user@example.com',
+ 'user:@example.com',
+ 'user:password@example.com',
+ ],
+ invalid: [
+ 'user:user:password@example.com',
+ '@example.com',
+ ':@example.com',
+ ':example.com',
+ ],
+ });
+ });
+
+ it('should allow user to skip URL length validation', () => {
+ test({
+ validator: 'isURL',
+ args: [{ validate_length: false }],
+ valid: [
+ 'http://foobar.com/f',
+ `http://foobar.com/${new Array(2083).join('f')}`,
+ ],
+ invalid: [],
+ });
+ });
+
+ it('should allow user to configure the maximum URL length', () => {
+ test({
+ validator: 'isURL',
+ args: [{ max_allowed_length: 20 }],
+ valid: [
+ 'http://foobar.com/12', // 20 characters
+ 'http://foobar.com/',
+ ],
+ invalid: [
+ 'http://foobar.com/123', // 21 characters
+ 'http://foobar.com/1234567890',
+ ],
+ });
+ });
+
+ it('should validate URLs with port present', () => {
+ test({
+ validator: 'isURL',
+ args: [{ require_port: true }],
+ valid: [
+ 'http://user:pass@www.foobar.com:1',
+ 'http://user:@www.foobar.com:65535',
+ 'http://127.0.0.1:23',
+ 'http://10.0.0.0:256',
+ 'http://189.123.14.13:256',
+ 'http://duckduckgo.com:65535?q=%2F',
+ ],
+ invalid: [
+ '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',
+ ],
+ });
+ });
+
+ it('should validate MAC addresses', () => {
+ test({
+ validator: 'isMACAddress',
+ valid: [
+ 'ab:ab:ab:ab:ab:ab',
+ 'FF:FF:FF:FF:FF:FF',
+ '01:02:03:04:05:ab',
+ '01:AB:03:04:05:06',
+ 'A9 C5 D4 9F EB D3',
+ '01 02 03 04 05 ab',
+ '01-02-03-04-05-ab',
+ '0102.0304.05ab',
+ 'ab:ab:ab:ab:ab:ab:ab:ab',
+ 'FF:FF:FF:FF:FF:FF:FF:FF',
+ '01:02:03:04:05:06:07:ab',
+ '01:AB:03:04:05:06:07:08',
+ 'A9 C5 D4 9F EB D3 B6 65',
+ '01 02 03 04 05 06 07 ab',
+ '01-02-03-04-05-06-07-ab',
+ '0102.0304.0506.07ab',
+ ],
+ invalid: [
+ 'abc',
+ '01:02:03:04:05',
+ '01:02:03:04:05:z0',
+ '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',
+ '0102.03:04.05ab',
+ '900f/dffs/sdea',
+ '01:02:03:04:05:06:07',
+ '01:02:03:04:05:06:07:z0',
+ '01:02:03:04:05:06::ab',
+ '1:2:3:4:5:6:7:8',
+ 'AB:CD:EF:GH:01:02:03:04',
+ 'A9C5 D4 9F EB D3 B6 65',
+ '01-02 03:04 05 06 07 ab',
+ '0102.03:04.0506.07ab',
+ '900f/dffs/sdea/54gh',
+ ],
+ });
+ test({
+ validator: 'isMACAddress',
+ args: [{
+ eui: '48',
+ }],
+ valid: [
+ 'ab:ab:ab:ab:ab:ab',
+ 'FF:FF:FF:FF:FF:FF',
+ '01:02:03:04:05:ab',
+ '01:AB:03:04:05:06',
+ 'A9 C5 D4 9F EB D3',
+ '01 02 03 04 05 ab',
+ '01-02-03-04-05-ab',
+ '0102.0304.05ab',
+ ],
+ invalid: [
+ 'ab:ab:ab:ab:ab:ab:ab:ab',
+ 'FF:FF:FF:FF:FF:FF:FF:FF',
+ '01:02:03:04:05:06:07:ab',
+ '01:AB:03:04:05:06:07:08',
+ 'A9 C5 D4 9F EB D3 B6 65',
+ '01 02 03 04 05 06 07 ab',
+ '01-02-03-04-05-06-07-ab',
+ '0102.0304.0506.07ab',
+ ],
+ });
+ test({
+ validator: 'isMACAddress',
+ args: [{
+ eui: '64',
+ }],
+ valid: [
+ 'ab:ab:ab:ab:ab:ab:ab:ab',
+ 'FF:FF:FF:FF:FF:FF:FF:FF',
+ '01:02:03:04:05:06:07:ab',
+ '01:AB:03:04:05:06:07:08',
+ 'A9 C5 D4 9F EB D3 B6 65',
+ '01 02 03 04 05 06 07 ab',
+ '01-02-03-04-05-06-07-ab',
+ '0102.0304.0506.07ab',
+ ],
+ invalid: [
+ 'ab:ab:ab:ab:ab:ab',
+ 'FF:FF:FF:FF:FF:FF',
+ '01:02:03:04:05:ab',
+ '01:AB:03:04:05:06',
+ 'A9 C5 D4 9F EB D3',
+ '01 02 03 04 05 ab',
+ '01-02-03-04-05-ab',
+ '0102.0304.05ab',
+ ],
+ });
+ });
+
+ it('should validate MAC addresses without separator', () => {
+ test({
+ validator: 'isMACAddress',
+ args: [{
+ no_separators: true,
+ }],
+ valid: [
+ 'abababababab',
+ 'FFFFFFFFFFFF',
+ '0102030405ab',
+ '01AB03040506',
+ 'abababababababab',
+ 'FFFFFFFFFFFFFFFF',
+ '01020304050607ab',
+ '01AB030405060708',
+ ],
+ invalid: [
+ 'abc',
+ '01:02:03:04:05',
+ '01:02:03:04::ab',
+ '1:2:3:4:5:6',
+ 'AB:CD:EF:GH:01:02',
+ 'ab:ab:ab:ab:ab:ab',
+ 'FF:FF:FF:FF:FF:FF',
+ '01:02:03:04:05:ab',
+ '01:AB:03:04:05:06',
+ '0102030405',
+ '01020304ab',
+ '123456',
+ 'ABCDEFGH0102',
+ '01:02:03:04:05:06:07',
+ '01:02:03:04:05:06::ab',
+ '1:2:3:4:5:6:7:8',
+ 'AB:CD:EF:GH:01:02:03:04',
+ 'ab:ab:ab:ab:ab:ab:ab:ab',
+ 'FF:FF:FF:FF:FF:FF:FF:FF',
+ '01:02:03:04:05:06:07:ab',
+ '01:AB:03:04:05:06:07:08',
+ '01020304050607',
+ '010203040506ab',
+ '12345678',
+ 'ABCDEFGH01020304',
+ ],
+ });
+ test({
+ validator: 'isMACAddress',
+ args: [{
+ no_separators: true,
+ eui: '48',
+ }],
+ valid: [
+ 'abababababab',
+ 'FFFFFFFFFFFF',
+ '0102030405ab',
+ '01AB03040506',
+ ],
+ invalid: [
+ 'abababababababab',
+ 'FFFFFFFFFFFFFFFF',
+ '01020304050607ab',
+ '01AB030405060708',
+ ],
+ });
+ test({
+ validator: 'isMACAddress',
+ args: [{
+ no_separators: true,
+ eui: '64',
+ }],
+ valid: [
+ 'abababababababab',
+ 'FFFFFFFFFFFFFFFF',
+ '01020304050607ab',
+ '01AB030405060708',
+ ],
+ invalid: [
+ 'abababababab',
+ 'FFFFFFFFFFFF',
+ '0102030405ab',
+ '01AB03040506',
+ ],
+ });
+ });
+
+ it('should validate isIPRange', () => {
+ test({
+ validator: 'isIPRange',
+ valid: [
+ '127.0.0.1/24',
+ '0.0.0.0/0',
+ '255.255.255.0/32',
+ '::/0',
+ '::/128',
+ '2001::/128',
+ '2001:800::/128',
+ '::ffff:127.0.0.1/128',
+ ],
+ invalid: [
+ 'abc',
+ '127.200.230.1/35',
+ '127.200.230.1/-1',
+ '1.1.1.1/011',
+ '1.1.1/24.1',
+ '1.1.1.1/01',
+ '1.1.1.1/1.1',
+ '1.1.1.1/1.',
+ '1.1.1.1/1/1',
+ '1.1.1.1',
+ '::1',
+ '::1/164',
+ '2001::/240',
+ '2001::/-1',
+ '2001::/001',
+ '2001::/24.1',
+ '2001:db8:0000:1:1:1:1:1',
+ '::ffff:127.0.0.1',
+ ],
+ });
+ test({
+ validator: 'isIPRange',
+ args: [4],
+ valid: [
+ '127.0.0.1/1',
+ '0.0.0.0/1',
+ '255.255.255.255/1',
+ '1.2.3.4/1',
+ '255.0.0.1/1',
+ '0.0.1.1/1',
+ ],
+ invalid: [
+ 'abc',
+ '::1',
+ '2001:db8:0000:1:1:1:1:1',
+ '::ffff:127.0.0.1',
+ '137.132.10.01',
+ '0.256.0.256',
+ '255.256.255.256',
+ ],
+ });
+ test({
+ validator: 'isIPRange',
+ args: [6],
+ valid: [
+ '::1/1',
+ '2001:db8:0000:1:1:1:1:1/1',
+ '::ffff:127.0.0.1/1',
+ ],
+ invalid: [
+ 'abc',
+ '127.0.0.1',
+ '0.0.0.0',
+ '255.255.255.255',
+ '1.2.3.4',
+ '::ffff:287.0.0.1',
+ '::ffff:287.0.0.1/254',
+ '%',
+ 'fe80::1234%',
+ 'fe80::1234%1%3%4',
+ 'fe80%fe80%',
+ ],
+ });
+ test({
+ validator: 'isIPRange',
+ args: [10],
+ valid: [],
+ invalid: [
+ 'abc',
+ '127.0.0.1/1',
+ '0.0.0.0/1',
+ '255.255.255.255/1',
+ '1.2.3.4/1',
+ '::1/1',
+ '2001:db8:0000:1:1:1:1:1/1',
+ ],
+ });
+ });
+
+ it('should validate FQDN', () => {
+ test({
+ validator: 'isFQDN',
+ valid: [
+ 'domain.com',
+ 'dom.plato',
+ 'a.domain.co',
+ 'foo--bar.com',
+ 'xn--froschgrn-x9a.com',
+ 'rebecca.blackfriday',
+ '1337.com',
+ ],
+ invalid: [
+ 'abc',
+ '256.0.0.0',
+ '_.com',
+ '*.some.com',
+ 's!ome.com',
+ 'domain.com/',
+ '/more.com',
+ 'domain.com�',
+ 'domain.co\u00A0m',
+ 'domain.co\u1680m',
+ 'domain.co\u2006m',
+ 'domain.co\u2028m',
+ 'domain.co\u2029m',
+ 'domain.co\u202Fm',
+ 'domain.co\u205Fm',
+ 'domain.co\u3000m',
+ 'domain.com\uDC00',
+ 'domain.co\uEFFFm',
+ 'domain.co\uFDDAm',
+ 'domain.co\uFFF4m',
+ 'domain.com©',
+ 'example.0',
+ '192.168.0.9999',
+ '192.168.0',
+ ],
+ });
+ });
+ it('should validate FQDN with trailing dot option', () => {
+ test({
+ validator: 'isFQDN',
+ args: [
+ { allow_trailing_dot: true },
+ ],
+ valid: [
+ 'example.com.',
+ ],
+ });
+ });
+ it('should invalidate FQDN when not require_tld', () => {
+ test({
+ validator: 'isFQDN',
+ args: [
+ { require_tld: false },
+ ],
+ invalid: [
+ 'example.0',
+ '192.168.0',
+ '192.168.0.9999',
+ ],
+ });
+ });
+ it('should validate FQDN when not require_tld but allow_numeric_tld', () => {
+ test({
+ validator: 'isFQDN',
+ args: [
+ { allow_numeric_tld: true, require_tld: false },
+ ],
+ valid: [
+ 'example.0',
+ '192.168.0',
+ '192.168.0.9999',
+ ],
+ });
+ });
+ it('should validate FQDN with wildcard option', () => {
+ test({
+ validator: 'isFQDN',
+ args: [
+ { allow_wildcard: true },
+ ],
+ valid: [
+ '*.example.com',
+ '*.shop.example.com',
+ ],
+ });
+ });
+ it('should validate FQDN with required allow_trailing_dot, allow_underscores and allow_numeric_tld options', () => {
+ test({
+ validator: 'isFQDN',
+ args: [
+ { allow_trailing_dot: true, allow_underscores: true, allow_numeric_tld: true },
+ ],
+ valid: [
+ 'abc.efg.g1h.',
+ 'as1s.sad3s.ssa2d.',
+ ],
+ });
+ });
+
+ it('should validate alpha strings', () => {
+ test({
+ validator: 'isAlpha',
+ valid: [
+ 'abc',
+ 'ABC',
+ 'FoObar',
+ ],
+ invalid: [
+ 'abc1',
+ ' foo ',
+ '',
+ 'ÄBC',
+ 'FÜübar',
+ 'Jön',
+ 'Heiß',
+ ],
+ });
+ });
+
+ it('should validate alpha string with ignored characters', () => {
+ test({
+ validator: 'isAlpha',
+ args: ['en-US', { ignore: '- /' }], // ignore [space-/]
+ valid: [
+ 'en-US',
+ 'this is a valid alpha string',
+ 'us/usa',
+ ],
+ invalid: [
+ '1. this is not a valid alpha string',
+ 'this$is also not a valid.alpha string',
+ 'this is also not a valid alpha string.',
+ ],
+ });
+
+ test({
+ validator: 'isAlpha',
+ args: ['en-US', { ignore: /[\s/-]/g }], // ignore [space -]
+ valid: [
+ 'en-US',
+ 'this is a valid alpha string',
+ ],
+ invalid: [
+ '1. this is not a valid alpha string',
+ 'this$is also not a valid.alpha string',
+ 'this is also not a valid alpha string.',
+ ],
+ });
+
+ test({
+ validator: 'isAlpha',
+ args: ['en-US', { ignore: 1234 }], // invalid ignore matcher
+ error: [
+ 'alpha',
+ ],
+ });
+ });
+
+ it('should validate Azerbaijani alpha strings', () => {
+ test({
+ validator: 'isAlpha',
+ args: ['az-AZ'],
+ valid: [
+ 'Azərbaycan',
+ 'Bakı',
+ 'üöğıəçş',
+ 'sizAzərbaycanlaşdırılmışlardansınızmı',
+ 'dahaBirDüzgünString',
+ 'abcçdeəfgğhxıijkqlmnoöprsştuüvyz',
+ ],
+ invalid: [
+ 'rəqəm1',
+ ' foo ',
+ '',
+ 'ab(cd)',
+ 'simvol@',
+ 'wəkil',
+ ],
+ });
+ });
+
+ it('should validate bulgarian alpha strings', () => {
+ test({
+ validator: 'isAlpha',
+ args: ['bg-BG'],
+ valid: [
+ 'абв',
+ 'АБВ',
+ 'жаба',
+ 'яГоДа',
+ ],
+ invalid: [
+ 'abc1',
+ ' foo ',
+ '',
+ 'ЁЧПС',
+ '_аз_обичам_обувки_',
+ 'ехо!',
+ ],
+ });
+ });
+
+ it('should validate Bengali alpha strings', () => {
+ test({
+ validator: 'isAlpha',
+ args: ['bn-BD'],
+ valid: [
+ 'অয়াওর',
+ 'ফগফদ্রত',
+ 'ফদ্ম্যতভ',
+ 'বেরেওভচনভন',
+ 'আমারবাসগা',
+ ],
+ invalid: [
+ 'দাস২৩৪',
+ ' দ্গফহ্নভ ',
+ '',
+ '(গফদ)',
+ ],
+ });
+ });
+
+ it('should validate czech alpha strings', () => {
+ test({
+ validator: 'isAlpha',
+ args: ['cs-CZ'],
+ valid: [
+ 'žluťoučký',
+ 'KŮŇ',
+ 'Pěl',
+ 'Ďábelské',
+ 'ódy',
+ ],
+ invalid: [
+ 'ábc1',
+ ' fůj ',
+ '',
+ ],
+ });
+ });
+
+ it('should validate slovak alpha strings', () => {
+ test({
+ validator: 'isAlpha',
+ args: ['sk-SK'],
+ valid: [
+ 'môj',
+ 'ľúbím',
+ 'mäkčeň',
+ 'stĹp',
+ 'vŕba',
+ 'ňorimberk',
+ 'ťava',
+ 'žanéta',
+ 'Ďábelské',
+ 'ódy',
+ ],
+ invalid: [
+ '1moj',
+ '你好世界',
+ ' Привет мир ',
+ 'مرحبا العا ',
+ ],
+ });
+ });
+
+ it('should validate danish alpha strings', () => {
+ test({
+ validator: 'isAlpha',
+ args: ['da-DK'],
+ valid: [
+ 'aøå',
+ 'Ære',
+ 'Øre',
+ 'Åre',
+ ],
+ invalid: [
+ 'äbc123',
+ 'ÄBC11',
+ '',
+ ],
+ });
+ });
+
+ it('should validate dutch alpha strings', () => {
+ test({
+ validator: 'isAlpha',
+ args: ['nl-NL'],
+ valid: [
+ 'Kán',
+ 'één',
+ 'vóór',
+ 'nú',
+ 'héél',
+ ],
+ invalid: [
+ 'äca ',
+ 'abcß',
+ 'Øre',
+ ],
+ });
+ });
+
+ it('should validate german alpha strings', () => {
+ test({
+ validator: 'isAlpha',
+ args: ['de-DE'],
+ valid: [
+ 'äbc',
+ 'ÄBC',
+ 'FöÖbär',
+ 'Heiß',
+ ],
+ invalid: [
+ 'äbc1',
+ ' föö ',
+ '',
+ ],
+ });
+ });
+
+ it('should validate hungarian alpha strings', () => {
+ test({
+ validator: 'isAlpha',
+ args: ['hu-HU'],
+ valid: [
+ 'árvíztűrőtükörfúrógép',
+ 'ÁRVÍZTŰRŐTÜKÖRFÚRÓGÉP',
+ ],
+ invalid: [
+ 'äbc1',
+ ' fäö ',
+ 'Heiß',
+ '',
+ ],
+ });
+ });
+
+ it('should validate portuguese alpha strings', () => {
+ test({
+ validator: 'isAlpha',
+ args: ['pt-PT'],
+ valid: [
+ 'palíndromo',
+ 'órgão',
+ 'qwértyúão',
+ 'àäãcëüïÄÏÜ',
+ ],
+ invalid: [
+ '12abc',
+ 'Heiß',
+ 'Øre',
+ 'æøå',
+ '',
+ ],
+ });
+ });
+
+ it('should validate italian alpha strings', () => {
+ test({
+ validator: 'isAlpha',
+ args: ['it-IT'],
+ valid: [
+ 'àéèìîóòù',
+ 'correnti',
+ 'DEFINIZIONE',
+ 'compilazione',
+ 'metró',
+ 'pèsca',
+ 'PÉSCA',
+ 'genî',
+ ],
+ invalid: [
+ 'äbc123',
+ 'ÄBC11',
+ 'æøå',
+ '',
+ ],
+ });
+ });
+
+ it('should validate Japanese alpha strings', () => {
+ test({
+ validator: 'isAlpha',
+ args: ['ja-JP'],
+ valid: [
+ 'あいうえお',
+ 'がぎぐげご',
+ 'ぁぃぅぇぉ',
+ 'アイウエオ',
+ 'ァィゥェ',
+ 'アイウエオ',
+ '吾輩は猫である',
+ '臥薪嘗胆',
+ '新世紀エヴァンゲリオン',
+ '天国と地獄',
+ '七人の侍',
+ 'シン・ウルトラマン',
+ ],
+ invalid: [
+ 'あいう123',
+ 'abcあいう',
+ '1984',
+ ],
+ });
+ });
+
+ it('should validate kazakh alpha strings', () => {
+ test({
+ validator: 'isAlpha',
+ args: ['kk-KZ'],
+ valid: [
+ 'Сәлем',
+ 'қанағаттандырылмағандықтарыңыздан',
+ 'Кешіріңіз',
+ 'Өкінішке',
+ 'Қайталаңызшы',
+ 'ағылшынша',
+ 'түсінбедім',
+ ],
+ invalid: [
+ 'Кешіріңіз1',
+ ' Кет бар ',
+ 'مرحبا العا',
+ ],
+ });
+ });
+
+ it('should validate Vietnamese alpha strings', () => {
+ test({
+ validator: 'isAlpha',
+ args: ['vi-VN'],
+ valid: [
+ 'thiến',
+ 'nghiêng',
+ 'xin',
+ 'chào',
+ 'thế',
+ 'giới',
+ ],
+ invalid: [
+ 'thầy3',
+ 'Ba gà',
+ '',
+ ],
+ });
+ });
+
+ it('should validate arabic alpha strings', () => {
+ test({
+ validator: 'isAlpha',
+ args: ['ar'],
+ valid: [
+ 'أبت',
+ 'اَبِتَثّجً',
+ ],
+ invalid: [
+ '١٢٣أبت',
+ '١٢٣',
+ 'abc1',
+ ' foo ',
+ '',
+ 'ÄBC',
+ 'FÜübar',
+ 'Jön',
+ 'Heiß',
+ ],
+ });
+ });
+
+ it('should validate farsi alpha strings', () => {
+ test({
+ validator: 'isAlpha',
+ args: ['fa-IR'],
+ valid: [
+ 'پدر',
+ 'مادر',
+ 'برادر',
+ 'خواهر',
+ ],
+ invalid: [
+ 'فارسی۱۲۳',
+ '۱۶۴',
+ 'abc1',
+ ' foo ',
+ '',
+ 'ÄBC',
+ 'FÜübar',
+ 'Jön',
+ 'Heiß',
+ ],
+ });
+ });
+
+ it('should validate finnish alpha strings', () => {
+ test({
+ validator: 'isAlpha',
+ args: ['fi-FI'],
+ valid: [
+ 'äiti',
+ 'Öljy',
+ 'Åke',
+ 'testÖ',
+ ],
+ invalid: [
+ 'AİıÖöÇ窺ĞğÜüZ',
+ 'äöå123',
+ '',
+ ],
+ });
+ });
+
+ it('should validate kurdish alpha strings', () => {
+ test({
+ validator: 'isAlpha',
+ args: ['ku-IQ'],
+ valid: [
+ 'ئؤڤگێ',
+ 'کوردستان',
+ ],
+ invalid: [
+ 'ئؤڤگێ١٢٣',
+ '١٢٣',
+ 'abc1',
+ ' foo ',
+ '',
+ 'ÄBC',
+ 'FÜübar',
+ 'Jön',
+ 'Heiß',
+ ],
+ });
+ });
+
+ it('should validate norwegian alpha strings', () => {
+ test({
+ validator: 'isAlpha',
+ args: ['nb-NO'],
+ valid: [
+ 'aøå',
+ 'Ære',
+ 'Øre',
+ 'Åre',
+ ],
+ invalid: [
+ 'äbc123',
+ 'ÄBC11',
+ '',
+ ],
+ });
+ });
+
+ it('should validate polish alpha strings', () => {
+ test({
+ validator: 'isAlpha',
+ args: ['pl-PL'],
+ valid: [
+ 'kreską',
+ 'zamknięte',
+ 'zwykłe',
+ 'kropką',
+ 'przyjęły',
+ 'święty',
+ 'Pozwól',
+ ],
+ invalid: [
+ '12řiď ',
+ 'blé!!',
+ 'föö!2!',
+ ],
+ });
+ });
+
+ it('should validate serbian cyrillic alpha strings', () => {
+ test({
+ validator: 'isAlpha',
+ args: ['sr-RS'],
+ valid: [
+ 'ШћжЂљЕ',
+ 'ЧПСТЋЏ',
+ ],
+ invalid: [
+ 'řiď ',
+ 'blé33!!',
+ 'föö!!',
+ ],
+ });
+ });
+
+ it('should validate serbian latin alpha strings', () => {
+ test({
+ validator: 'isAlpha',
+ args: ['sr-RS@latin'],
+ valid: [
+ 'ŠAabčšđćž',
+ 'ŠATROĆčđš',
+ ],
+ invalid: [
+ '12řiď ',
+ 'blé!!',
+ 'föö!2!',
+ ],
+ });
+ });
+
+ it('should validate spanish alpha strings', () => {
+ test({
+ validator: 'isAlpha',
+ args: ['es-ES'],
+ valid: [
+ 'ábcó',
+ 'ÁBCÓ',
+ 'dormís',
+ 'volvés',
+ 'español',
+ ],
+ invalid: [
+ 'äca ',
+ 'abcß',
+ 'föö!!',
+ ],
+ });
+ });
+
+ it('should validate swedish alpha strings', () => {
+ test({
+ validator: 'isAlpha',
+ args: ['sv-SE'],
+ valid: [
+ 'religiös',
+ 'stjäla',
+ 'västgöte',
+ 'Åre',
+ ],
+ invalid: [
+ 'AİıÖöÇ窺ĞğÜüZ',
+ 'religiös23',
+ '',
+ ],
+ });
+ });
+
+ it('should validate defined arabic locales alpha strings', () => {
+ test({
+ validator: 'isAlpha',
+ args: ['ar-SY'],
+ valid: [
+ 'أبت',
+ 'اَبِتَثّجً',
+ ],
+ invalid: [
+ '١٢٣أبت',
+ '١٢٣',
+ 'abc1',
+ ' foo ',
+ '',
+ 'ÄBC',
+ 'FÜübar',
+ 'Jön',
+ 'Heiß',
+ ],
+ });
+ });
+
+ it('should validate turkish alpha strings', () => {
+ test({
+ validator: 'isAlpha',
+ args: ['tr-TR'],
+ valid: [
+ 'AİıÖöÇ窺ĞğÜüZ',
+ ],
+ invalid: [
+ '0AİıÖöÇ窺ĞğÜüZ1',
+ ' AİıÖöÇ窺ĞğÜüZ ',
+ 'abc1',
+ ' foo ',
+ '',
+ 'ÄBC',
+ 'Heiß',
+ ],
+ });
+ });
+
+ it('should validate urkrainian alpha strings', () => {
+ test({
+ validator: 'isAlpha',
+ args: ['uk-UA'],
+ valid: [
+ 'АБВГҐДЕЄЖЗИIЇЙКЛМНОПРСТУФХЦШЩЬЮЯ',
+ ],
+ invalid: [
+ '0AİıÖöÇ窺ĞğÜüZ1',
+ ' AİıÖöÇ窺ĞğÜüZ ',
+ 'abc1',
+ ' foo ',
+ '',
+ 'ÄBC',
+ 'Heiß',
+ 'ЫыЪъЭэ',
+ ],
+ });
+ });
+
+ it('should validate greek alpha strings', () => {
+ test({
+ validator: 'isAlpha',
+ args: ['el-GR'],
+ valid: [
+ 'αβγδεζηθικλμνξοπρςστυφχψω',
+ 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ',
+ 'άέήίΰϊϋόύώ',
+ 'ΆΈΉΊΪΫΎΏ',
+ ],
+ invalid: [
+ '0AİıÖöÇ窺ĞğÜüZ1',
+ ' AİıÖöÇ窺ĞğÜüZ ',
+ 'ÄBC',
+ 'Heiß',
+ 'ЫыЪъЭэ',
+ '120',
+ 'jαckγ',
+ ],
+ });
+ });
+
+ it('should validate Hebrew alpha strings', () => {
+ test({
+ validator: 'isAlpha',
+ args: ['he'],
+ valid: [
+ 'בדיקה',
+ 'שלום',
+ ],
+ invalid: [
+ 'בדיקה123',
+ ' foo ',
+ 'abc1',
+ '',
+ ],
+ });
+ });
+
+ it('should validate Hindi alpha strings', () => {
+ test({
+ validator: 'isAlpha',
+ args: ['hi-IN'],
+ valid: [
+ 'अतअपनाअपनीअपनेअभीअंदरआदिआपइत्यादिइनइनकाइन्हींइन्हेंइन्होंइसइसकाइसकीइसकेइसमेंइसीइसेउनउनकाउनकीउनकेउनकोउन्हींउन्हेंउन्होंउसउसकेउसीउसेएकएवंएसऐसेऔरकईकरकरताकरतेकरनाकरनेकरेंकहतेकहाकाकाफ़ीकिकितनाकिन्हेंकिन्होंकियाकिरकिसकिसीकिसेकीकुछकुलकेकोकोईकौनकौनसागयाघरजबजहाँजाजितनाजिनजिन्हेंजिन्होंजिसजिसेजीधरजैसाजैसेजोतकतबतरहतिनतिन्हेंतिन्होंतिसतिसेतोथाथीथेदबारादियादुसरादूसरेदोद्वाराननकेनहींनानिहायतनीचेनेपरपहलेपूरापेफिरबनीबहीबहुतबादबालाबिलकुलभीभीतरमगरमानोमेमेंयदियहयहाँयहीयायिहयेरखेंरहारहेऱ्वासालिएलियेलेकिनववग़ैरहवर्गवहवहाँवहींवालेवुहवेवोसकतासकतेसबसेसभीसाथसाबुतसाभसारासेसोसंगहीहुआहुईहुएहैहैंहोहोताहोतीहोतेहोनाहोने',
+ 'इन्हें',
+ ],
+ invalid: [
+ 'अत०२३४५६७८९',
+ 'अत 12',
+ ' अत ',
+ 'abc1',
+ 'abc',
+ '',
+ ],
+ });
+ });
+
+ it('should validate Tamil alpha strings', () => {
+ test({
+ validator: 'isAlpha',
+ args: ['ta-IN'],
+ valid: [
+ 'அஆஇஈஉஊஎஏஐஒஓஔகஙசஞடணதநபமயரலவழளறனஶஜஷஸஹ',
+ 'தமிழ்',
+ ],
+ invalid: [
+ 'தமிழ்123',
+ 'தமிழ் ',
+ 'தமிழ்.',
+ 'abc',
+ '',
+ ],
+ });
+ });
+ it('should validate Telugu alpha strings', () => {
+ test({
+ validator: 'isAlpha',
+ args: ['te-IN'],
+ valid: [
+ 'అఆఇఈఉఊఋఌఎఏఐఒఓఔకఖగఘఙచఛజఝఞటఠడఢణతథదధనపఫబభమయరలవశషసహ',
+ 'తెలుగు',
+ ],
+ invalid: ['తెలుగు123', 'తెలుగు.', 'abc', ''],
+ });
+ });
+ it('should validate Kannada alpha strings', () => {
+ test({
+ validator: 'isAlpha',
+ args: ['kn-IN'],
+ valid: [
+ 'ಅಆಇಈಉಊಋಎಏಐಒಓಔಕಖಗಘಙಚಛಜಝಞಟಠಡಢಣತಥದಧನಪಫಬಭಮಯರಲವಶಷಸಹಳ',
+ 'ಕನ್ನಡ',
+ ],
+ invalid: ['ಕನ್ನಡ123', 'ಕನ್ನಡ.', 'abc', ''],
+ });
+ });
+ it('should validate Malayalam alpha strings', () => {
+ test({
+ validator: 'isAlpha',
+ args: ['ml-IN'],
+ valid: [
+ 'അആഇഈഉഊഋഎഏഐഒഓഔകഖഗഘങചഛജഝഞടഠഡഢണതഥദധനപഫബഭമയരലവശഷസഹള',
+ 'മലയാളം',
+ ],
+ invalid: ['മലയാളം123', 'മലയാളം.', 'abc', ''],
+ });
+ });
+ it('should validate Gujarati alpha strings', () => {
+ test({
+ validator: 'isAlpha',
+ args: ['gu-IN'],
+ valid: [
+ 'અઆઇઈઉઊઋએઐઓઔકખગઘચછજઝટઠડઢણતથદધનપફબભમયરલવશષસહળ',
+ 'ગુજરાતી',
+ ],
+ invalid: ['ગુજરાતી123', 'ગુજરાતી.', 'abc', ''],
+ });
+ });
+ it('should validate Punjabi alpha strings', () => {
+ test({
+ validator: 'isAlpha',
+ args: ['pa-IN'],
+ valid: [
+ 'ਅਆਇਈਉਊਏਐਓਔਕਖਗਘਙਚਛਜਝਞਟਠਡਢਣਤਥਦਧਨਪਫਬਭਮਯਰਲਵਸ਼ਸਹ',
+ 'ਪੰਜਾਬੀ',
+ ],
+ invalid: ['ਪੰਜਾਬੀ123', 'ਪੰਜਾਬੀ.', 'abc', ''],
+ });
+ });
+ it('should validate Odia alpha strings', () => {
+ test({
+ validator: 'isAlpha',
+ args: ['or-IN'],
+ valid: [
+ 'ଅଆଇଈଉଊଋଌଏଐଓଔକଖଗଘଙଚଛଜଝଞଟଠଡଢଣତଥଦଧନପଫବଭମଯରଲଶଷସହଳ',
+ 'ଓଡ଼ିଆ',
+ ],
+ invalid: ['ଓଡ଼ିଆ123', 'ଓଡ଼ିଆ.', 'abc', ''],
+ });
+ });
+ it('should validate Bengali alpha strings', () => {
+ test({
+ validator: 'isAlpha',
+ args: ['bn-IN'],
+ valid: [
+ 'অআইঈউঊঋএঐওঔকখগঘঙচছজঝঞটঠডঢণতথদধনপফবভমযরলশষসহ',
+ 'বাংলা',
+ ],
+ invalid: ['বাংলা123', 'বাংলা.', 'abc', ''],
+ });
+ });
+ it('should validate persian alpha strings', () => {
+ test({
+ validator: 'isAlpha',
+ args: ['fa-IR'],
+ valid: [
+ 'تست',
+ 'عزیزم',
+ 'ح',
+ ],
+ invalid: [
+ 'تست 1',
+ ' عزیزم ',
+ '',
+ ],
+ });
+ });
+
+ it('should validate Thai alpha strings', () => {
+ test({
+ validator: 'isAlpha',
+ args: ['th-TH'],
+ valid: [
+ 'สวัสดี',
+ 'ยินดีต้อนรับ เทสเคส',
+ ],
+ invalid: [
+ 'สวัสดีHi',
+ '123 ยินดีต้อนรับ',
+ 'ยินดีต้อนรับ-๑๒๓',
+ ],
+ });
+ });
+
+ it('should validate Korea alpha strings', () => {
+ test({
+ validator: 'isAlpha',
+ args: ['ko-KR'],
+ valid: [
+ 'ㄱ',
+ 'ㅑ',
+ 'ㄱㄴㄷㅏㅕ',
+ '세종대왕',
+ '나랏말싸미듕귁에달아문자와로서르사맛디아니할쎄',
+ ],
+ invalid: [
+ 'abc',
+ '123',
+ '흥선대원군 문호개방',
+ '1592년임진왜란',
+ '대한민국!',
+ ],
+ });
+ });
+
+ it('should validate Sinhala alpha strings', () => {
+ test({
+ validator: 'isAlpha',
+ args: ['si-LK'],
+ valid: [
+ 'චතුර',
+ 'කචටදබ',
+ 'ඎඏදාෛපසුගො',
+ ],
+ invalid: [
+ 'ஆஐअतක',
+ 'කචට 12',
+ ' ඎ ',
+ 'abc1',
+ 'abc',
+ '',
+ ],
+ });
+ });
+
+ it('should validate Esperanto alpha strings', () => {
+ test({
+ validator: 'isAlpha',
+ args: ['eo'],
+ valid: [
+ 'saluton',
+ 'eĥoŝanĝoĉiuĵaŭde',
+ 'EĤOŜANĜOĈIUĴAŬDE',
+ 'Esperanto',
+ 'LaŭLudovikoZamenhofBongustasFreŝaĈeĥaManĝaĵoKunSpicoj',
+ ],
+ invalid: [
+ 'qwxyz',
+ '1887',
+ 'qwxyz 1887',
+ ],
+ });
+ });
+
+ it('should error on invalid locale', () => {
+ test({
+ validator: 'isAlpha',
+ args: ['is-NOT'],
+ error: [
+ 'abc',
+ 'ABC',
+ ],
+ });
+ });
+
+ it('should validate alphanumeric strings', () => {
+ test({
+ validator: 'isAlphanumeric',
+ valid: [
+ 'abc123',
+ 'ABC11',
+ ],
+ invalid: [
+ 'abc ',
+ 'foo!!',
+ 'ÄBC',
+ 'FÜübar',
+ 'Jön',
+ ],
+ });
+ });
+
+ it('should validate alphanumeric string with ignored characters', () => {
+ test({
+ validator: 'isAlphanumeric',
+ args: ['en-US', { ignore: '@_- ' }], // ignore [@ space _ -]
+ valid: [
+ 'Hello@123',
+ 'this is a valid alphaNumeric string',
+ 'En-US @ alpha_numeric',
+ ],
+ invalid: [
+ 'In*Valid',
+ 'hello$123',
+ '{invalid}',
+ ],
+ });
+
+ test({
+ validator: 'isAlphanumeric',
+ args: ['en-US', { ignore: /[\s/-]/g }], // ignore [space -]
+ valid: [
+ 'en-US',
+ 'this is a valid alphaNumeric string',
+ ],
+ invalid: [
+ 'INVALID$ AlphaNum Str',
+ 'hello@123',
+ 'abc*123',
+ ],
+ });
+
+ test({
+ validator: 'isAlphanumeric',
+ args: ['en-US', { ignore: 1234 }], // invalid ignore matcher (ignore should be instance of a String or RegExp)
+ error: [
+ 'alpha',
+ ],
+ });
+ });
+
+ it('should validate defined english aliases', () => {
+ test({
+ validator: 'isAlphanumeric',
+ args: ['en-GB'],
+ valid: [
+ 'abc123',
+ 'ABC11',
+ ],
+ invalid: [
+ 'abc ',
+ 'foo!!',
+ 'ÄBC',
+ 'FÜübar',
+ 'Jön',
+ ],
+ });
+ });
+
+ it('should validate Azerbaijani alphanumeric strings', () => {
+ test({
+ validator: 'isAlphanumeric',
+ args: ['az-AZ'],
+ valid: [
+ 'Azərbaycan',
+ 'Bakı',
+ 'abc1',
+ 'abcç2',
+ '3kərə4kərə',
+ ],
+ invalid: [
+ ' foo1 ',
+ '',
+ 'ab(cd)',
+ 'simvol@',
+ 'wəkil',
+ ],
+ });
+ });
+
+ it('should validate bulgarian alphanumeric strings', () => {
+ test({
+ validator: 'isAlphanumeric',
+ args: ['bg-BG'],
+ valid: [
+ 'абв1',
+ '4АБ5В6',
+ 'жаба',
+ 'яГоДа2',
+ 'йЮя',
+ '123',
+ ],
+ invalid: [
+ ' ',
+ '789 ',
+ 'hello000',
+ ],
+ });
+ });
+
+ it('should validate Bengali alphanumeric strings', () => {
+ test({
+ validator: 'isAlphanumeric',
+ args: ['bn-BD'],
+ valid: [
+ 'দ্গজ্ঞহ্রত্য১২৩',
+ 'দ্গগফ৮৯০',
+ 'চব৩৬৫ভবচ',
+ '১২৩৪',
+ '৩৪২৩৪দফজ্ঞদফ',
+ ],
+ invalid: [
+ ' ',
+ '১২৩ ',
+ 'hel৩২0',
+ ],
+ });
+ });
+
+ it('should validate czech alphanumeric strings', () => {
+ test({
+ validator: 'isAlphanumeric',
+ args: ['cs-CZ'],
+ valid: [
+ 'řiť123',
+ 'KŮŇ11',
+ ],
+ invalid: [
+ 'řiď ',
+ 'blé!!',
+ ],
+ });
+ });
+
+ it('should validate slovak alphanumeric strings', () => {
+ test({
+ validator: 'isAlphanumeric',
+ args: ['sk-SK'],
+ valid: [
+ '1môj',
+ '2ľúbím',
+ '3mäkčeň',
+ '4stĹp',
+ '5vŕba',
+ '6ňorimberk',
+ '7ťava',
+ '8žanéta',
+ '9Ďábelské',
+ '10ódy',
+ ],
+ invalid: [
+ '1moj!',
+ '你好世界',
+ ' Привет мир ',
+ ],
+ });
+ });
+
+ it('should validate danish alphanumeric strings', () => {
+ test({
+ validator: 'isAlphanumeric',
+ args: ['da-DK'],
+ valid: [
+ 'ÆØÅ123',
+ 'Ære321',
+ '321Øre',
+ '123Åre',
+ ],
+ invalid: [
+ 'äbc123',
+ 'ÄBC11',
+ '',
+ ],
+ });
+ });
+
+ it('should validate dutch alphanumeric strings', () => {
+ test({
+ validator: 'isAlphanumeric',
+ args: ['nl-NL'],
+ valid: [
+ 'Kán123',
+ 'één354',
+ 'v4óór',
+ 'nú234',
+ 'hé54él',
+ ],
+ invalid: [
+ '1äca ',
+ 'ab3cß',
+ 'Øre',
+ ],
+ });
+ });
+
+ it('should validate finnish alphanumeric strings', () => {
+ test({
+ validator: 'isAlphanumeric',
+ args: ['fi-FI'],
+ valid: [
+ 'äiti124',
+ 'ÖLJY1234',
+ '123Åke',
+ '451åå23',
+ ],
+ invalid: [
+ 'AİıÖöÇ窺ĞğÜüZ',
+ 'foo!!',
+ '',
+ ],
+ });
+ });
+
+ it('should validate german alphanumeric strings', () => {
+ test({
+ validator: 'isAlphanumeric',
+ args: ['de-DE'],
+ valid: [
+ 'äbc123',
+ 'ÄBC11',
+ ],
+ invalid: [
+ 'äca ',
+ 'föö!!',
+ ],
+ });
+ });
+
+ it('should validate hungarian alphanumeric strings', () => {
+ test({
+ validator: 'isAlphanumeric',
+ args: ['hu-HU'],
+ valid: [
+ '0árvíztűrőtükörfúrógép123',
+ '0ÁRVÍZTŰRŐTÜKÖRFÚRÓGÉP123',
+ ],
+ invalid: [
+ '1időúr!',
+ 'äbc1',
+ ' fäö ',
+ 'Heiß!',
+ '',
+ ],
+ });
+ });
+
+ it('should validate portuguese alphanumeric strings', () => {
+ test({
+ validator: 'isAlphanumeric',
+ args: ['pt-PT'],
+ valid: [
+ 'palíndromo',
+ '2órgão',
+ 'qwértyúão9',
+ 'àäãcë4üïÄÏÜ',
+ ],
+ invalid: [
+ '!abc',
+ 'Heiß',
+ 'Øre',
+ 'æøå',
+ '',
+ ],
+ });
+ });
+
+ it('should validate italian alphanumeric strings', () => {
+ test({
+ validator: 'isAlphanumeric',
+ args: ['it-IT'],
+ valid: [
+ '123àéèìîóòù',
+ '123correnti',
+ 'DEFINIZIONE321',
+ 'compil123azione',
+ 'met23ró',
+ 'pès56ca',
+ 'PÉS45CA',
+ 'gen45î',
+ ],
+ invalid: [
+ 'äbc123',
+ 'ÄBC11',
+ 'æøå',
+ '',
+ ],
+ });
+ });
+
+ it('should validate spanish alphanumeric strings', () => {
+ test({
+ validator: 'isAlphanumeric',
+ args: ['es-ES'],
+ valid: [
+ 'ábcó123',
+ 'ÁBCÓ11',
+ ],
+ invalid: [
+ 'äca ',
+ 'abcß',
+ 'föö!!',
+ ],
+ });
+ });
+
+ it('should validate Vietnamese alphanumeric strings', () => {
+ test({
+ validator: 'isAlphanumeric',
+ args: ['vi-VN'],
+ valid: [
+ 'Thầy3',
+ '3Gà',
+ ],
+ invalid: [
+ 'toang!',
+ 'Cậu Vàng',
+ ],
+ });
+ });
+
+ it('should validate arabic alphanumeric strings', () => {
+ test({
+ validator: 'isAlphanumeric',
+ args: ['ar'],
+ valid: [
+ 'أبت123',
+ 'أبتَُِ١٢٣',
+ ],
+ invalid: [
+ 'äca ',
+ 'abcß',
+ 'föö!!',
+ ],
+ });
+ });
+
+ it('should validate Hindi alphanumeric strings', () => {
+ test({
+ validator: 'isAlphanumeric',
+ args: ['hi-IN'],
+ valid: [
+ 'अतअपनाअपनीअपनेअभीअंदरआदिआपइत्यादिइनइनकाइन्हींइन्हेंइन्होंइसइसकाइसकीइसकेइसमेंइसीइसेउनउनकाउनकीउनकेउनकोउन्हींउन्हेंउन्होंउसउसकेउसीउसेएकएवंएसऐसेऔरकईकरकरताकरतेकरनाकरनेकरेंकहतेकहाकाकाफ़ीकिकितनाकिन्हेंकिन्होंकियाकिरकिसकिसीकिसेकीकुछकुलकेकोकोईकौनकौनसागयाघरजबजहाँजाजितनाजिनजिन्हेंजिन्होंजिसजिसेजीधरजैसाजैसेजोतकतबतरहतिनतिन्हेंतिन्होंतिसतिसेतोथाथीथेदबारादियादुसरादूसरेदोद्वाराननकेनहींनानिहायतनीचेनेपरपहलेपूरापेफिरबनीबहीबहुतबादबालाबिलकुलभीभीतरमगरमानोमेमेंयदियहयहाँयहीयायिहयेरखेंरहारहेऱ्वासालिएलियेलेकिनववग़ैरहवर्गवहवहाँवहींवालेवुहवेवोसकतासकतेसबसेसभीसाथसाबुतसाभसारासेसोसंगहीहुआहुईहुएहैहैंहोहोताहोतीहोतेहोनाहोने०२३४५६७८९',
+ 'इन्हें४५६७८९',
+ ],
+ invalid: [
+ 'अत ०२३४५६७८९',
+ ' ३४५६७८९',
+ '12 ',
+ ' अत ',
+ 'abc1',
+ 'abc',
+ '',
+ ],
+ });
+ });
+
+ it('should validate farsi alphanumeric strings', () => {
+ test({
+ validator: 'isAlphanumeric',
+ args: ['fa-IR'],
+ valid: [
+ 'پارسی۱۲۳',
+ '۱۴۵۶',
+ 'مژگان9',
+ ],
+ invalid: [
+ 'äca ',
+ 'abcßة',
+ 'föö!!',
+ '٤٥٦',
+ ],
+ });
+ });
+
+ it('should validate Japanese alphanumeric strings', () => {
+ test({
+ validator: 'isAlphanumeric',
+ args: ['ja-JP'],
+ valid: [
+ 'あいうえお123',
+ '123がぎぐげご',
+ 'ぁぃぅぇぉ',
+ 'アイウエオ',
+ 'ァィゥェ',
+ 'アイウエオ',
+ '20世紀少年',
+ '華氏451度',
+ ],
+ invalid: [
+ ' あいう123 ',
+ 'abcあいう',
+ '生きろ!!',
+ ],
+ });
+ });
+
+ it('should validate kazakh alphanumeric strings', () => {
+ test({
+ validator: 'isAlphanumeric',
+ args: ['kk-KZ'],
+ valid: [
+ 'Сәлем777',
+ '123Бәсе',
+ 'солай',
+ 'Жиенсу',
+ '90тоқсан',
+ 'жалғыз',
+ '570бердім',
+ ],
+ invalid: [
+ ' кешіріңіз ',
+ 'abcағылшынша',
+ 'мүмкін!!',
+ ],
+ });
+ });
+
+ it('should validate kurdish alphanumeric strings', () => {
+ test({
+ validator: 'isAlphanumeric',
+ args: ['ku-IQ'],
+ valid: [
+ 'ئؤڤگێ١٢٣',
+ ],
+ invalid: [
+ 'äca ',
+ 'abcß',
+ 'föö!!',
+ ],
+ });
+ });
+
+ it('should validate defined arabic aliases', () => {
+ test({
+ validator: 'isAlphanumeric',
+ args: ['ar-SY'],
+ valid: [
+ 'أبت123',
+ 'أبتَُِ١٢٣',
+ ],
+ invalid: [
+ 'abc ',
+ 'foo!!',
+ 'ÄBC',
+ 'FÜübar',
+ 'Jön',
+ ],
+ });
+ });
+
+ it('should validate norwegian alphanumeric strings', () => {
+ test({
+ validator: 'isAlphanumeric',
+ args: ['nb-NO'],
+ valid: [
+ 'ÆØÅ123',
+ 'Ære321',
+ '321Øre',
+ '123Åre',
+ ],
+ invalid: [
+ 'äbc123',
+ 'ÄBC11',
+ '',
+ ],
+ });
+ });
+
+ it('should validate polish alphanumeric strings', () => {
+ test({
+ validator: 'isAlphanumeric',
+ args: ['pl-PL'],
+ valid: [
+ 'kre123ską',
+ 'zam21knięte',
+ 'zw23ykłe',
+ '123',
+ 'prz23yjęły',
+ 'świ23ęty',
+ 'Poz1322wól',
+ ],
+ invalid: [
+ '12řiď ',
+ 'blé!!',
+ 'föö!2!',
+ ],
+ });
+ });
+
+ it('should validate serbian cyrillic alphanumeric strings', () => {
+ test({
+ validator: 'isAlphanumeric',
+ args: ['sr-RS'],
+ valid: [
+ 'ШћжЂљЕ123',
+ 'ЧПСТ132ЋЏ',
+ ],
+ invalid: [
+ 'řiď ',
+ 'blé!!',
+ 'föö!!',
+ ],
+ });
+ });
+
+ it('should validate serbian latin alphanumeric strings', () => {
+ test({
+ validator: 'isAlphanumeric',
+ args: ['sr-RS@latin'],
+ valid: [
+ 'ŠAabčšđćž123',
+ 'ŠATRO11Ćčđš',
+ ],
+ invalid: [
+ 'řiď ',
+ 'blé!!',
+ 'föö!!',
+ ],
+ });
+ });
+
+ it('should validate swedish alphanumeric strings', () => {
+ test({
+ validator: 'isAlphanumeric',
+ args: ['sv-SE'],
+ valid: [
+ 'religiös13',
+ 'st23jäla',
+ 'västgöte123',
+ '123Åre',
+ ],
+ invalid: [
+ 'AİıÖöÇ窺ĞğÜüZ',
+ 'foo!!',
+ '',
+ ],
+ });
+ });
+
+ it('should validate turkish alphanumeric strings', () => {
+ test({
+ validator: 'isAlphanumeric',
+ args: ['tr-TR'],
+ valid: [
+ 'AİıÖöÇ窺ĞğÜüZ123',
+ ],
+ invalid: [
+ 'AİıÖöÇ窺ĞğÜüZ ',
+ 'foo!!',
+ 'ÄBC',
+ ],
+ });
+ });
+
+ it('should validate urkrainian alphanumeric strings', () => {
+ test({
+ validator: 'isAlphanumeric',
+ args: ['uk-UA'],
+ valid: [
+ 'АБВГҐДЕЄЖЗИIЇЙКЛМНОПРСТУФХЦШЩЬЮЯ123',
+ ],
+ invalid: [
+ 'éeoc ',
+ 'foo!!',
+ 'ÄBC',
+ 'ЫыЪъЭэ',
+ ],
+ });
+ });
+
+ it('should validate greek alphanumeric strings', () => {
+ test({
+ validator: 'isAlphanumeric',
+ args: ['el-GR'],
+ valid: [
+ 'αβγδεζηθικλμνξοπρςστυφχψω',
+ 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ',
+ '20θ',
+ '1234568960',
+ ],
+ invalid: [
+ '0AİıÖöÇ窺ĞğÜüZ1',
+ ' AİıÖöÇ窺ĞğÜüZ ',
+ 'ÄBC',
+ 'Heiß',
+ 'ЫыЪъЭэ',
+ 'jαckγ',
+ ],
+ });
+ });
+
+ it('should validate Hebrew alphanumeric strings', () => {
+ test({
+ validator: 'isAlphanumeric',
+ args: ['he'],
+ valid: [
+ 'אבג123',
+ 'שלום11',
+ ],
+ invalid: [
+ 'אבג ',
+ 'לא!!',
+ 'abc',
+ ' foo ',
+ ],
+ });
+ });
+
+ it('should validate Thai alphanumeric strings', () => {
+ test({
+ validator: 'isAlphanumeric',
+ args: ['th-TH'],
+ valid: [
+ 'สวัสดี ๑๒๓',
+ 'ยินดีต้อนรับทั้ง ๒ คน',
+ ],
+ invalid: [
+ '1.สวัสดี',
+ 'ยินดีต้อนรับทั้ง 2 คน',
+ ],
+ });
+ });
+
+ it('should validate Korea alphanumeric strings', () => {
+ test({
+ validator: 'isAlphanumeric',
+ args: ['ko-KR'],
+ valid: [
+ '2002',
+ '훈민정음',
+ '1446년훈민정음반포',
+ ],
+ invalid: [
+ '2022!',
+ '2019 코로나시작',
+ '1.로렘입숨',
+ ],
+ });
+ });
+
+ it('should validate Sinhala alphanumeric strings', () => {
+ test({
+ validator: 'isAlphanumeric',
+ args: ['si-LK'],
+ valid: [
+ 'චතුර',
+ 'කචට12',
+ 'ඎඏදාෛපසුගො2',
+ '1234',
+ ],
+ invalid: [
+ 'ஆஐअतක',
+ 'කචට 12',
+ ' ඎ ',
+ 'a1234',
+ 'abc',
+ '',
+ ],
+ });
+ });
+
+ it('should validate Esperanto alphanumeric strings', () => {
+ test({
+ validator: 'isAlphanumeric',
+ args: ['eo'],
+ valid: [
+ 'saluton',
+ 'eĥoŝanĝoĉiuĵaŭde0123456789',
+ 'EĤOŜANĜOĈIUĴAŬDE0123456789',
+ 'Esperanto1887',
+ 'LaŭLudovikoZamenhofBongustasFreŝaĈeĥaManĝaĵoKunSpicoj',
+ ],
+ invalid: [
+ 'qwxyz',
+ 'qwxyz 1887',
+ ],
+ });
+ });
+ it('should validate Tamil alphanumeric strings', () => {
+ test({
+ validator: 'isAlphanumeric',
+ args: ['ta-IN'],
+ valid: [
+ 'தமிழ்',
+ 'தமிழ்123',
+ 'அஆஇஈ123',
+ 'தமிழ்123.45',
+ '123.45',
+ 'தமிழ்.',
+ ],
+ invalid: [
+ 'தமிழ் ',
+ 'abc',
+ '',
+ ],
+ });
+ });
+
+ it('should validate Telugu alphanumeric strings', () => {
+ test({
+ validator: 'isAlphanumeric',
+ args: ['te-IN'],
+ valid: [
+ 'తెలుగు',
+ 'తెలుగు123',
+ 'అఆఇఈ123',
+ 'తెలుగు123.45',
+ '123.45',
+ 'తెలుగు.',
+ ],
+ invalid: [
+ 'abc',
+ '',
+ ],
+ });
+ });
+
+ it('should validate Kannada alphanumeric strings', () => {
+ test({
+ validator: 'isAlphanumeric',
+ args: ['kn-IN'],
+ valid: [
+ 'ಕನ್ನಡ',
+ 'ಕನ್ನಡ123',
+ 'ಅಆಇಈ123',
+ 'ಕನ್ನಡ123.45',
+ '123.45',
+ 'ಕನ್ನಡ.',
+ ],
+ invalid: [
+ 'abc',
+ '',
+ ],
+ });
+ });
+
+ it('should validate Malayalam alphanumeric strings', () => {
+ test({
+ validator: 'isAlphanumeric',
+ args: ['ml-IN'],
+ valid: [
+ 'മലയാളം',
+ 'മലയാളം123',
+ 'അആഇഈ123',
+ 'മലയാളം123.45',
+ '123.45',
+ 'മലയാളം.',
+ ],
+ invalid: [
+ 'abc',
+ '',
+ ],
+ });
+ });
+
+ it('should validate Gujarati alphanumeric strings', () => {
+ test({
+ validator: 'isAlphanumeric',
+ args: ['gu-IN'],
+ valid: [
+ 'ગુજરાતી',
+ 'ગુજરાતી123',
+ 'અઆઇઈ123',
+ 'ગુજરાતી123.45',
+ '123.45',
+ 'ગુજરાતી.',
+ ],
+ invalid: [
+ 'abc',
+ '',
+ ],
+ });
+ });
+
+ it('should validate Punjabi alphanumeric strings', () => {
+ test({
+ validator: 'isAlphanumeric',
+ args: ['pa-IN'],
+ valid: [
+ 'ਪੰਜਾਬੀ',
+ 'ਪੰਜਾਬੀ123',
+ 'ਅਆਇਈ123',
+ 'ਪੰਜਾਬੀ123.45',
+ '123.45',
+ 'ਪੰਜਾਬੀ.',
+ ],
+ invalid: [
+ 'abc',
+ '',
+ ],
+ });
+ });
+
+ it('should validate Odia alphanumeric strings', () => {
+ test({
+ validator: 'isAlphanumeric',
+ args: ['or-IN'],
+ valid: [
+ 'ଓଡ଼ିଆ',
+ 'ଓଡ଼ିଆ123',
+ 'ଅଆଇଈ123',
+ 'ଓଡ଼ିଆ123.45',
+ '123.45',
+ 'ଓଡ଼ିଆ.',
+ ],
+ invalid: [
+ 'abc',
+ '',
+ ],
+ });
+ });
+
+ it('should validate Bengali alphanumeric strings', () => {
+ test({
+ validator: 'isAlphanumeric',
+ args: ['bn-IN'],
+ valid: [
+ 'বাংলা',
+ 'বাংলা১২৩',
+ 'অআইঈ১২৩',
+ '১২৩৪৫৬৭৮৯০',
+ 'বাংলা১২৩',
+ '১২৩৪৫',
+ 'বাংলা',
+ ],
+ invalid: [
+ 'abc',
+ 'বাংলা123',
+ '123',
+ 'বাংলা ১২৩',
+ 'বাংলা,১২৩',
+ '১২৩٫৪৫',
+ '',
+ ],
+ });
+ });
+
+ it('should error on invalid locale', () => {
+ test({
+ validator: 'isAlphanumeric',
+ args: ['is-NOT'],
+ error: [
+ '1234568960',
+ 'abc123',
+ ],
+ });
+ });
+
+ it('should validate numeric strings', () => {
+ test({
+ validator: 'isNumeric',
+ valid: [
+ '123',
+ '00123',
+ '-00123',
+ '0',
+ '-0',
+ '+123',
+ '123.123',
+ '+000000',
+ ],
+ invalid: [
+ ' ',
+ '',
+ '.',
+ ],
+ });
+ });
+
+ it('should validate numeric strings without symbols', () => {
+ test({
+ validator: 'isNumeric',
+ args: [{
+ no_symbols: true,
+ }],
+ valid: [
+ '123',
+ '00123',
+ '0',
+ ],
+ invalid: [
+ '-0',
+ '+000000',
+ '',
+ '+123',
+ '123.123',
+ '-00123',
+ ' ',
+ '.',
+ ],
+ });
+ });
+
+ it('should validate numeric strings with locale', () => {
+ test({
+ validator: 'isNumeric',
+ args: [{
+ locale: 'fr-FR',
+ }],
+ valid: [
+ '123',
+ '00123',
+ '-00123',
+ '0',
+ '-0',
+ '+123',
+ '123,123',
+ '+000000',
+ ],
+ invalid: [
+ ' ',
+ '',
+ ',',
+ ],
+ });
+ });
+
+ it('should validate numeric strings with locale', () => {
+ test({
+ validator: 'isNumeric',
+ args: [{
+ locale: 'fr-CA',
+ }],
+ valid: [
+ '123',
+ '00123',
+ '-00123',
+ '0',
+ '-0',
+ '+123',
+ '123,123',
+ '+000000',
+ ],
+ invalid: [
+ ' ',
+ '',
+ '.',
+ ],
+ });
+ });
+
+ it('should validate ports', () => {
+ test({
+ validator: 'isPort',
+ valid: [
+ '0',
+ '22',
+ '80',
+ '443',
+ '3000',
+ '8080',
+ '65535',
+ ],
+ invalid: [
+ '',
+ '-1',
+ '65536',
+ '0080',
+ ],
+ });
+ });
+
+ it('should validate passport number', () => {
+ test({
+ validator: 'isPassportNumber',
+ args: ['AM'],
+ valid: [
+ 'AF0549358',
+ ],
+ invalid: [
+ 'A1054935',
+ ],
+ });
+
+
+ test({
+ validator: 'isPassportNumber',
+ args: ['ID'],
+ valid: [
+ 'C1253473',
+ 'B5948378',
+ 'A4859472',
+ ],
+ invalid: [
+ 'D39481728',
+ 'A-3847362',
+ '324132132',
+ ],
+ });
+
+ test({
+ validator: 'isPassportNumber',
+ args: ['AR'],
+ valid: [
+ 'AAC811035',
+ ],
+ invalid: [
+ 'A11811035',
+ ],
+ });
+
+ test({
+ validator: 'isPassportNumber',
+ args: ['AT'],
+ valid: [
+ 'P 1630837',
+ 'P 4366918',
+ ],
+ invalid: [
+ '0 1630837',
+ ],
+ });
+
+ test({
+ validator: 'isPassportNumber',
+ args: ['AU'],
+ valid: [
+ 'N0995852',
+ 'L4819236',
+ ],
+ invalid: [
+ '1A012345',
+ ],
+ });
+
+ test({
+ validator: 'isPassportNumber',
+ args: ['AZ'],
+ valid: [
+ 'A16175905',
+ 'A16175958',
+ ],
+ invalid: [
+ 'AZ1234584',
+ ],
+ });
+
+ test({
+ validator: 'isPassportNumber',
+ args: ['BE'],
+ valid: [
+ 'EM000000',
+ 'LA080402',
+ ],
+ invalid: [
+ '00123456',
+ ],
+ });
+
+ test({
+ validator: 'isPassportNumber',
+ args: ['BG'],
+ valid: [
+ '346395366',
+ '039903356',
+ ],
+ invalid: [
+ 'ABC123456',
+ ],
+ });
+
+ test({
+ validator: 'isPassportNumber',
+ args: ['BR'],
+ valid: [
+ 'FZ973689',
+ 'GH231233',
+ ],
+ invalid: [
+ 'ABX29332',
+ ],
+ });
+
+ test({
+ validator: 'isPassportNumber',
+ args: ['BY'],
+ valid: [
+ 'MP3899901',
+ ],
+ invalid: [
+ '345333454',
+ 'FG53334542',
+ ],
+ });
+
+ test({
+ validator: 'isPassportNumber',
+ args: ['CA'],
+ valid: [
+ 'GA302922',
+ 'ZE000509',
+ 'A123456AB',
+ 'Z556378HG',
+ ],
+ invalid: [
+ 'AB0123456',
+ 'AZ556378H',
+ '556378HCX',
+ '556378432',
+ '5563784',
+ '#B12345FD',
+ 'A43F12354',
+ ],
+ });
+
+ test({
+ validator: 'isPassportNumber',
+ args: ['CH'],
+ valid: [
+ 'S1100409',
+ 'S5200073',
+ 'X4028791',
+ ],
+ invalid: [
+ 'AB123456',
+ ],
+ });
+
+ test({
+ validator: 'isPassportNumber',
+ args: ['CN'],
+ valid: [
+ 'G25352389',
+ 'E00160027',
+ 'EA1234567',
+ ],
+ invalid: [
+ 'K0123456',
+ 'E-1234567',
+ 'G.1234567',
+ 'GA1234567',
+ 'EI1234567',
+ 'GO1234567',
+ ],
+ });
+
+ test({
+ validator: 'isPassportNumber',
+ args: ['CY'],
+ valid: [
+ 'K00000413',
+ ],
+ invalid: [
+ 'K10100',
+ ],
+ });
+
+ test({
+ validator: 'isPassportNumber',
+ args: ['CZ'],
+ valid: [
+ '99003853',
+ '42747260',
+ ],
+ invalid: [
+ '012345678',
+ 'AB123456',
+ ],
+ });
+
+ test({
+ validator: 'isPassportNumber',
+ args: ['DE'],
+ valid: [
+ 'C01X00T47',
+ 'C26VMVVC3',
+ ],
+ invalid: [
+ 'AS0123456',
+ 'A012345678',
+ ],
+ });
+
+ test({
+ validator: 'isPassportNumber',
+ args: ['DK'],
+ valid: [
+ '900010172',
+ ],
+ invalid: [
+ '01234567',
+ 'K01234567',
+ ],
+ });
+
+ test({
+ validator: 'isPassportNumber',
+ args: ['DZ'],
+ valid: [
+ '855609385',
+ '154472412',
+ '197025599',
+ ],
+ invalid: [
+ 'AS0123456',
+ 'A012345678',
+ '0123456789',
+ '12345678',
+ '98KK54321',
+ ],
+ });
+
+ test({
+ validator: 'isPassportNumber',
+ args: ['EE'],
+ valid: [
+ 'K4218285',
+ 'K3295867',
+ 'KB0167630',
+ 'VD0023777',
+ ],
+ invalid: [
+ 'K01234567',
+ 'KB00112233',
+ ],
+ });
+
+ test({
+ validator: 'isPassportNumber',
+ args: ['ES'],
+ valid: [
+ 'AF238143',
+ 'ZAB000254',
+ ],
+ invalid: [
+ 'AF01234567',
+ ],
+ });
+
+ test({
+ validator: 'isPassportNumber',
+ args: ['FI'],
+ valid: [
+ 'XP8271602',
+ 'XD8500003',
+ ],
+ invalid: [
+ 'A01234567',
+ 'ABC012345',
+ ],
+ });
+
+ test({
+ validator: 'isPassportNumber',
+ args: ['FR'],
+ valid: [
+ '10CV28144',
+ '60RF19342',
+ '05RP34083',
+ ],
+ invalid: [
+ '012345678',
+ 'AB0123456',
+ '01C234567',
+ ],
+ });
+
+ test({
+ validator: 'isPassportNumber',
+ args: ['GB'],
+ valid: [
+ '925076473',
+ '107182890',
+ '104121156',
+ ],
+ invalid: [
+ 'A012345678',
+ 'K000000000',
+ '0123456789',
+ ],
+ });
+
+ test({
+ validator: 'isPassportNumber',
+ args: ['GR'],
+ valid: [
+ 'AE0000005',
+ 'AK0219304',
+ ],
+ invalid: [
+ 'A01234567',
+ '012345678',
+ ],
+ });
+
+ test({
+ validator: 'isPassportNumber',
+ args: ['HR'],
+ valid: [
+ '007007007',
+ '138463188',
+ ],
+ invalid: [
+ 'A01234567',
+ '00112233',
+ ],
+ });
+
+ test({
+ validator: 'isPassportNumber',
+ args: ['HU'],
+ valid: [
+ 'ZA084505',
+ 'BA0006902',
+ ],
+ invalid: [
+ 'A01234567',
+ '012345678',
+ ],
+ });
+
+ test({
+ validator: 'isPassportNumber',
+ args: ['IE'],
+ valid: [
+ 'D23145890',
+ 'X65097105',
+ 'XN0019390',
+ ],
+ invalid: [
+ 'XND012345',
+ '0123456789',
+ ],
+ });
+
+ test({
+ validator: 'isPassportNumber',
+ args: ['IN'],
+ valid: [
+ 'A-1234567',
+ 'A1234567',
+ 'X0019390',
+ ],
+ invalid: [
+ 'AB-1234567',
+ '0123456789',
+ ],
+ });
+
+ test({
+ validator: 'isPassportNumber',
+ args: ['IR'],
+ valid: [
+ 'J97634522',
+ 'A01234567',
+ 'Z11977831',
+ ],
+ invalid: [
+ 'A0123456',
+ 'A0123456Z',
+ '012345678',
+ ],
+ });
+
+ test({
+ validator: 'isPassportNumber',
+ args: ['IS'],
+ valid: [
+ 'A2040611',
+ 'A1197783',
+ ],
+ invalid: [
+ 'K0000000',
+ '01234567',
+ ],
+ });
+
+ test({
+ validator: 'isPassportNumber',
+ args: ['IT'],
+ valid: [
+ 'YA8335453',
+ 'KK0000000',
+ ],
+ invalid: [
+ '01234567',
+ 'KAK001122',
+ ],
+ });
+
+ test({
+ validator: 'isPassportNumber',
+ args: ['JM'],
+ valid: [
+ 'A0123456',
+ ],
+ invalid: [
+ 's0123456',
+ 'a01234567',
+ ],
+ });
+
+ test({
+ validator: 'isPassportNumber',
+ args: ['JP'],
+ valid: [
+ 'NH1106002',
+ 'TE3180251',
+ 'XS1234567',
+ ],
+ invalid: [
+ 'X12345678',
+ '012345678',
+ ],
+ });
+
+ test({
+ validator: 'isPassportNumber',
+ args: ['KR'],
+ valid: [
+ 'M35772699',
+ 'M70689098',
+ ],
+ invalid: [
+ 'X12345678',
+ '012345678',
+ ],
+ });
+
+ test({
+ validator: 'isPassportNumber',
+ args: ['KZ'],
+ valid: [
+ 'A0123456',
+ 'b0123456',
+ ],
+ invalid: [
+ '01234567',
+ 'bb0123456',
+ ],
+ });
+
+ test({
+ validator: 'isPassportNumber',
+ args: ['LI'],
+ valid: [
+ 'a01234',
+ 'f01234',
+ ],
+ invalid: [
+ '012345',
+ ],
+ });
+
+ test({
+ validator: 'isPassportNumber',
+ args: ['LT'],
+ valid: [
+ '20200997',
+ 'LB311756',
+ ],
+ invalid: [
+ 'LB01234567',
+ ],
+ });
+
+ test({
+ validator: 'isPassportNumber',
+ args: ['LU'],
+ valid: [
+ 'JCU9J4T2',
+ 'JC4E7L2H',
+ ],
+ invalid: [
+ 'JCU9J4T',
+ 'JC4E7L2H0',
+ ],
+ });
+
+ test({
+ validator: 'isPassportNumber',
+ args: ['LV'],
+ valid: [
+ 'LV9000339',
+ 'LV4017173',
+ ],
+ invalid: [
+ 'LV01234567',
+ '4017173LV',
+ ],
+ });
+ test({
+ validator: 'isPassportNumber',
+ args: ['LY'],
+ valid: [
+ 'P79JF34X',
+ 'RJ45H4V2',
+ ],
+ invalid: [
+ 'P79JF34',
+ 'RJ45H4V2C',
+ 'RJ4-H4V2',
+ ],
+ });
+
+ test({
+ validator: 'isPassportNumber',
+ args: ['MT'],
+ valid: [
+ '1026564',
+ ],
+ invalid: [
+ '01234567',
+ 'MT01234',
+ ],
+ });
+ test({
+ validator: 'isPassportNumber',
+ args: ['MZ'],
+ valid: [
+ 'AB0808212',
+ '08AB12123',
+ ],
+ invalid: [
+ '1AB011241',
+ '1AB01121',
+ 'ABAB01121',
+ ],
+ });
+ test({
+ validator: 'isPassportNumber',
+ args: ['MY'],
+ valid: [
+ 'A00000000',
+ 'H12345678',
+ 'K43143233',
+ ],
+ invalid: [
+ 'A1234567',
+ 'C01234567',
+ ],
+ });
+
+ test({
+ validator: 'isPassportNumber',
+ args: ['MX'],
+ valid: [
+ '43986369222',
+ '01234567890',
+ ],
+ invalid: [
+ 'ABC34567890',
+ '34567890',
+ ],
+ });
+
+ test({
+ validator: 'isPassportNumber',
+ args: ['NL'],
+ valid: [
+ 'XTR110131',
+ 'XR1001R58',
+ ],
+ invalid: [
+ 'XTR11013R',
+ 'XR1001R58A',
+ ],
+ });
+ test({
+ validator: 'isPassportNumber',
+ args: ['PK'],
+ valid: [
+ 'QZ1791293',
+ 'XR1001458',
+ ],
+ invalid: [
+ 'XTR11013R',
+ 'XR1001R58A',
+ ],
+ });
+
+ test({
+ validator: 'isPassportNumber',
+ args: ['PH'],
+ valid: [
+ 'X123456',
+ 'XY123456',
+ 'XY1234567',
+ 'X1234567Y',
+ ],
+ invalid: [
+ 'XY12345',
+ 'X12345Z',
+ 'XY12345Z',
+ ],
+ });
+
+ test({
+ validator: 'isPassportNumber',
+ args: ['NZ'],
+ valid: [
+ 'Lf012345',
+ 'La012345',
+ 'Ld012345',
+ 'Lh012345',
+ 'ea012345',
+ 'ep012345',
+ 'n012345',
+ ],
+ invalid: [
+ 'Lp012345',
+ 'nd012345',
+ 'ed012345',
+ 'eh012345',
+ 'ef012345',
+ ],
+ });
+
+ test({
+ validator: 'isPassportNumber',
+ args: ['PL'],
+ valid: [
+ 'ZS 0000177',
+ 'AN 3000011',
+ ],
+ invalid: [
+ 'A1 0000177',
+ '012345678',
+ ],
+ });
+
+ test({
+ validator: 'isPassportNumber',
+ args: ['PT'],
+ valid: [
+ 'I700044',
+ 'K453286',
+ ],
+ invalid: [
+ '0700044',
+ 'K4532861',
+ ],
+ });
+
+ test({
+ validator: 'isPassportNumber',
+ args: ['RO'],
+ valid: [
+ '05485968',
+ '040005646',
+ ],
+ invalid: [
+ 'R05485968',
+ '0511060461',
+ ],
+ });
+
+ test({
+ validator: 'isPassportNumber',
+ args: ['RU'],
+ valid: [
+ '2 32 636829',
+ '012 345321',
+ '439863692',
+ ],
+ invalid: [
+ 'A 2R YU46J0',
+ '01A 3D5321',
+ 'SF233D53T',
+ '12345678',
+ '1234567890',
+ ],
+ });
+
+ test({
+ validator: 'isPassportNumber',
+ args: ['SE'],
+ valid: [
+ '59000001',
+ '56702928',
+ ],
+ invalid: [
+ 'SE012345',
+ '012345678',
+ ],
+ });
+
+ test({
+ validator: 'isPassportNumber',
+ args: ['SL'],
+ valid: [
+ 'PB0036440',
+ 'PB1390281',
+ ],
+ invalid: [
+ 'SL0123456',
+ 'P01234567',
+ ],
+ });
+
+ test({
+ validator: 'isPassportNumber',
+ args: ['SK'],
+ valid: [
+ 'P0000000',
+ ],
+ invalid: [
+ 'SK012345',
+ '012345678',
+ ],
+ });
+
+ test({
+ validator: 'isPassportNumber',
+ args: ['TH'],
+ valid: [
+ 'A123456',
+ 'B1234567',
+ 'CD123456',
+ 'EF1234567',
+ ],
+ invalid: [
+ '123456',
+ '1234567',
+ '010485371AA',
+ ],
+ });
+
+ test({
+ validator: 'isPassportNumber',
+ args: ['TR'],
+ valid: [
+ 'U 06764100',
+ 'U 01048537',
+ ],
+ invalid: [
+ '06764100U',
+ '010485371',
+ ],
+ });
+
+ test({
+ validator: 'isPassportNumber',
+ args: ['UA'],
+ valid: [
+ 'EH345655',
+ 'EK000001',
+ 'AP841503',
+ ],
+ invalid: [
+ '01234567',
+ '012345EH',
+ 'A012345P',
+ ],
+ });
+
+ test({
+ validator: 'isPassportNumber',
+ args: ['US'],
+ valid: [
+ '790369937',
+ '340007237',
+ 'A90583942',
+ 'E00007734',
+ ],
+ invalid: [
+ 'US0123456',
+ '0123456US',
+ '7903699371',
+ '90583942',
+ 'E000077341',
+ ],
+ });
+
+ test({
+ validator: 'isPassportNumber',
+ args: ['ZA'],
+ valid: [
+ 'T12345678',
+ 'A12345678',
+ 'M12345678',
+ 'D12345678',
+ ],
+ invalid: [
+ '123456789',
+ 'Z12345678',
+ ],
+ });
+ });
+
+ it('should validate decimal numbers', () => {
+ test({
+ validator: 'isDecimal',
+ valid: [
+ '123',
+ '00123',
+ '-00123',
+ '0',
+ '-0',
+ '+123',
+ '0.01',
+ '.1',
+ '1.0',
+ '-.25',
+ '-0',
+ '0.0000000000001',
+ ],
+ invalid: [
+ '0,01',
+ ',1',
+ '1,0',
+ '-,25',
+ '0,0000000000001',
+ '0٫01',
+ '٫1',
+ '1٫0',
+ '-٫25',
+ '0٫0000000000001',
+ '....',
+ ' ',
+ '',
+ '-',
+ '+',
+ '.',
+ '0.1a',
+ 'a',
+ '\n',
+ ],
+ });
+
+ test({
+ validator: 'isDecimal',
+ args: [{ locale: 'en-AU' }],
+ valid: [
+ '123',
+ '00123',
+ '-00123',
+ '0',
+ '-0',
+ '+123',
+ '0.01',
+ '.1',
+ '1.0',
+ '-.25',
+ '-0',
+ '0.0000000000001',
+ ],
+ invalid: [
+ '0,01',
+ ',1',
+ '1,0',
+ '-,25',
+ '0,0000000000001',
+ '0٫01',
+ '٫1',
+ '1٫0',
+ '-٫25',
+ '0٫0000000000001',
+ '....',
+ ' ',
+ '',
+ '-',
+ '+',
+ '.',
+ '0.1a',
+ 'a',
+ '\n',
+ ],
+ });
+
+ test({
+ validator: 'isDecimal',
+ args: [{ locale: ['bg-BG'] }],
+ valid: [
+ '123',
+ '00123',
+ '-00123',
+ '0',
+ '-0',
+ '+123',
+ '0,01',
+ ',1',
+ '1,0',
+ '-,25',
+ '-0',
+ '0,0000000000001',
+ ],
+ invalid: [
+ '0.0000000000001',
+ '0.01',
+ '.1',
+ '1.0',
+ '-.25',
+ '0٫01',
+ '٫1',
+ '1٫0',
+ '-٫25',
+ '0٫0000000000001',
+ '....',
+ ' ',
+ '',
+ '-',
+ '+',
+ '.',
+ '0.1a',
+ 'a',
+ '\n',
+ ],
+ });
+
+ test({
+ validator: 'isDecimal',
+ args: [{ locale: ['cs-CZ'] }],
+ valid: [
+ '123',
+ '00123',
+ '-00123',
+ '0',
+ '-0',
+ '+123',
+ '0,01',
+ ',1',
+ '1,0',
+ '-,25',
+ '-0',
+ '0,0000000000001',
+ ],
+ invalid: [
+ '0.0000000000001',
+ '0.01',
+ '.1',
+ '1.0',
+ '-.25',
+ '0٫01',
+ '٫1',
+ '1٫0',
+ '-٫25',
+ '0٫0000000000001',
+ '....',
+ ' ',
+ '',
+ '-',
+ '+',
+ '.',
+ '0.1a',
+ 'a',
+ '\n',
+ ],
+ });
+
+ test({
+ validator: 'isDecimal',
+ args: [{ locale: ['ar-JO'] }],
+ valid: [
+ '123',
+ '00123',
+ '-00123',
+ '0',
+ '-0',
+ '+123',
+ '0٫01',
+ '٫1',
+ '1٫0',
+ '-٫25',
+ '-0',
+ '0٫0000000000001',
+ ],
+ invalid: [
+ '0,0000000000001',
+ '0,01',
+ ',1',
+ '1,0',
+ '-,25',
+ '0.0000000000001',
+ '0.01',
+ '.1',
+ '1.0',
+ '-.25',
+ '....',
+ ' ',
+ '',
+ '-',
+ '+',
+ '.',
+ '0.1a',
+ 'a',
+ '\n',
+ ],
+ });
+
+ test({
+ validator: 'isDecimal',
+ args: [{ locale: ['ar-EG'] }],
+ valid: [
+ '0.01',
+ ],
+ invalid: [
+ '0,01',
+ ],
+ });
+
+ test({
+ validator: 'isDecimal',
+ args: [{ locale: ['en-ZM'] }],
+ valid: [
+ '0,01',
+ ],
+ invalid: [
+ '0.01',
+ ],
+ });
+
+ test({
+ validator: 'isDecimal',
+ args: [{ force_decimal: true }],
+ valid: [
+ '0.01',
+ '.1',
+ '1.0',
+ '-.25',
+ '0.0000000000001',
+ ],
+ invalid: [
+ '-0',
+ '123',
+ '00123',
+ '-00123',
+ '0',
+ '-0',
+ '+123',
+ '0,0000000000001',
+ '0,01',
+ ',1',
+ '1,0',
+ '-,25',
+ '....',
+ ' ',
+ '',
+ '-',
+ '+',
+ '.',
+ '0.1a',
+ 'a',
+ '\n',
+ ],
+ });
+
+ test({
+ validator: 'isDecimal',
+ args: [{ decimal_digits: '2,3' }],
+ valid: [
+ '123',
+ '00123',
+ '-00123',
+ '0',
+ '-0',
+ '+123',
+ '0.01',
+ '1.043',
+ '.15',
+ '-.255',
+ '-0',
+ ],
+ invalid: [
+ '0.0000000000001',
+ '0.0',
+ '.1',
+ '1.0',
+ '-.2564',
+ '0.0',
+ '٫1',
+ '1٫0',
+ '-٫25',
+ '0٫0000000000001',
+ '....',
+ ' ',
+ '',
+ '-',
+ '+',
+ '.',
+ '0.1a',
+ 'a',
+ '\n',
+ ],
+ });
+ });
+
+ it('should error on invalid locale', () => {
+ test({
+ validator: 'isDecimal',
+ args: [{ locale: ['is-NOT'] }],
+ error: [
+ '123',
+ '0.01',
+ '0,01',
+ ],
+ });
+ });
+
+ it('should validate lowercase strings', () => {
+ test({
+ validator: 'isLowercase',
+ valid: [
+ 'abc',
+ 'abc123',
+ 'this is lowercase.',
+ 'tr竪s 端ber',
+ ],
+ invalid: [
+ 'fooBar',
+ '123A',
+ ],
+ });
+ });
+
+
+ it('should validate imei strings', () => {
+ test({
+ validator: 'isIMEI',
+ valid: [
+ '352099001761481',
+ '868932036356090',
+ '490154203237518',
+ '546918475942169',
+ '998227667144730',
+ '532729766805999',
+ ],
+ invalid: [
+ '490154203237517',
+ '3568680000414120',
+ '3520990017614823',
+ ],
+ });
+ });
+
+
+ it('should validate imei strings with hyphens', () => {
+ test({
+ validator: 'isIMEI',
+ args: [{ allow_hyphens: true }],
+ valid: [
+ '35-209900-176148-1',
+ '86-893203-635609-0',
+ '49-015420-323751-8',
+ '54-691847-594216-9',
+ '99-822766-714473-0',
+ '53-272976-680599-9',
+ ],
+ invalid: [
+ '49-015420-323751-7',
+ '35-686800-0041412-0',
+ '35-209900-1761482-3',
+ ],
+ });
+ });
+
+
+ it('should validate uppercase strings', () => {
+ test({
+ validator: 'isUppercase',
+ valid: [
+ 'ABC',
+ 'ABC123',
+ 'ALL CAPS IS FUN.',
+ ' .',
+ ],
+ invalid: [
+ 'fooBar',
+ '123abc',
+ ],
+ });
+ });
+
+ it('should validate integers', () => {
+ test({
+ validator: 'isInt',
+ valid: [
+ '13',
+ '123',
+ '0',
+ '123',
+ '-0',
+ '+1',
+ '01',
+ '-01',
+ '000',
+ ],
+ invalid: [
+ '100e10',
+ '123.123',
+ ' ',
+ '',
+ ],
+ });
+ test({
+ validator: 'isInt',
+ args: [{ allow_leading_zeroes: false }],
+ valid: [
+ '13',
+ '123',
+ '0',
+ '123',
+ '-0',
+ '+1',
+ ],
+ invalid: [
+ '01',
+ '-01',
+ '000',
+ '100e10',
+ '123.123',
+ ' ',
+ '',
+ ],
+ });
+ test({
+ validator: 'isInt',
+ args: [{ allow_leading_zeroes: true }],
+ valid: [
+ '13',
+ '123',
+ '0',
+ '123',
+ '-0',
+ '+1',
+ '01',
+ '-01',
+ '000',
+ '-000',
+ '+000',
+ ],
+ invalid: [
+ '100e10',
+ '123.123',
+ ' ',
+ '',
+ ],
+ });
+ test({
+ validator: 'isInt',
+ args: [{
+ min: 10,
+ }],
+ valid: [
+ '15',
+ '80',
+ '99',
+ ],
+ invalid: [
+ '9',
+ '6',
+ '3.2',
+ 'a',
+ ],
+ });
+ test({
+ validator: 'isInt',
+ args: [{
+ min: 10,
+ max: 15,
+ }],
+ valid: [
+ '15',
+ '11',
+ '13',
+ ],
+ invalid: [
+ '9',
+ '2',
+ '17',
+ '3.2',
+ '33',
+ 'a',
+ ],
+ });
+ test({
+ validator: 'isInt',
+ args: [{
+ gt: 10,
+ lt: 15,
+ }],
+ valid: [
+ '14',
+ '11',
+ '13',
+ ],
+ invalid: [
+ '10',
+ '15',
+ '17',
+ '3.2',
+ '33',
+ 'a',
+ ],
+ });
+ test({
+ validator: 'isInt',
+ args: [{
+ min: undefined,
+ max: undefined,
+ }],
+ valid: [
+ '143',
+ '15',
+ '767777575',
+ ],
+ invalid: [
+ '10.4',
+ 'bar',
+ '10a',
+ 'c44',
+ ],
+ });
+ test({
+ validator: 'isInt',
+ args: [{
+ gt: undefined,
+ lt: undefined,
+ }],
+ valid: [
+ '289373466',
+ '55',
+ '989',
+ ],
+ invalid: [
+ '10.4',
+ 'baz',
+ '66a',
+ 'c21',
+ ],
+ });
+ test({
+ validator: 'isInt',
+ args: [{
+ gt: null,
+ max: null,
+ }],
+ valid: [
+ '1',
+ '886',
+ '84512345',
+ ],
+ invalid: [
+ '10.4',
+ 'h',
+ '1.2',
+ '+',
+ ],
+ });
+ test({
+ validator: 'isInt',
+ args: [{
+ lt: null,
+ min: null,
+ }],
+ valid: [
+ '289373466',
+ '55',
+ '989',
+ ],
+ invalid: [
+ ',',
+ '+11212+',
+ 'fail',
+ '111987234i',
+ ],
+ });
+ });
+
+ it('should validate floats', () => {
+ test({
+ validator: 'isFloat',
+ valid: [
+ '123',
+ '123.',
+ '123.123',
+ '-123.123',
+ '-0.123',
+ '+0.123',
+ '0.123',
+ '.0',
+ '-.123',
+ '+.123',
+ '01.123',
+ '-0.22250738585072011e-307',
+ ],
+ invalid: [
+ '+',
+ '-',
+ ' ',
+ '',
+ '.',
+ ',',
+ 'foo',
+ '20.foo',
+ '2020-01-06T14:31:00.135Z',
+ ],
+ });
+
+ test({
+ validator: 'isFloat',
+ args: [{ locale: 'en-AU' }],
+ valid: [
+ '123',
+ '123.',
+ '123.123',
+ '-123.123',
+ '-0.123',
+ '+0.123',
+ '0.123',
+ '.0',
+ '-.123',
+ '+.123',
+ '01.123',
+ '-0.22250738585072011e-307',
+ ],
+ invalid: [
+ '123٫123',
+ '123,123',
+ ' ',
+ '',
+ '.',
+ 'foo',
+ ],
+ });
+
+ test({
+ validator: 'isFloat',
+ args: [{ locale: 'de-DE' }],
+ valid: [
+ '123',
+ '123,',
+ '123,123',
+ '-123,123',
+ '-0,123',
+ '+0,123',
+ '0,123',
+ ',0',
+ '-,123',
+ '+,123',
+ '01,123',
+ '-0,22250738585072011e-307',
+ ],
+ invalid: [
+ '123.123',
+ '123٫123',
+ ' ',
+ '',
+ '.',
+ 'foo',
+ ],
+ });
+
+ test({
+ validator: 'isFloat',
+ args: [{ locale: 'ar-JO' }],
+ valid: [
+ '123',
+ '123٫',
+ '123٫123',
+ '-123٫123',
+ '-0٫123',
+ '+0٫123',
+ '0٫123',
+ '٫0',
+ '-٫123',
+ '+٫123',
+ '01٫123',
+ '-0٫22250738585072011e-307',
+ ],
+ invalid: [
+ '123,123',
+ '123.123',
+ ' ',
+ '',
+ '.',
+ 'foo',
+ ],
+ });
+
+ test({
+ validator: 'isFloat',
+ args: [{
+ min: 3.7,
+ }],
+ valid: [
+ '3.888',
+ '3.92',
+ '4.5',
+ '50',
+ '3.7',
+ '3.71',
+ ],
+ invalid: [
+ '3.6',
+ '3.69',
+ '3',
+ '1.5',
+ 'a',
+ ],
+ });
+ test({
+ validator: 'isFloat',
+ args: [{
+ min: 0.1,
+ max: 1.0,
+ }],
+ valid: [
+ '0.1',
+ '1.0',
+ '0.15',
+ '0.33',
+ '0.57',
+ '0.7',
+ ],
+ invalid: [
+ '0',
+ '0.0',
+ 'a',
+ '1.3',
+ '0.05',
+ '5',
+ ],
+ });
+ test({
+ validator: 'isFloat',
+ args: [{
+ gt: -5.5,
+ lt: 10,
+ }],
+ valid: [
+ '9.9',
+ '1.0',
+ '0',
+ '-1',
+ '7',
+ '-5.4',
+ ],
+ invalid: [
+ '10',
+ '-5.5',
+ 'a',
+ '-20.3',
+ '20e3',
+ '10.00001',
+ ],
+ });
+ test({
+ validator: 'isFloat',
+ args: [{
+ min: -5.5,
+ max: 10,
+ gt: -5.5,
+ lt: 10,
+ }],
+ valid: [
+ '9.99999',
+ '-5.499999',
+ ],
+ invalid: [
+ '10',
+ '-5.5',
+ ],
+ });
+ test({
+ validator: 'isFloat',
+ args: [{
+ locale: 'de-DE',
+ min: 3.1,
+ }],
+ valid: [
+ '123',
+ '123,',
+ '123,123',
+ '3,1',
+ '3,100001',
+ ],
+ invalid: [
+ '3,09',
+ '-,123',
+ '+,123',
+ '01,123',
+ '-0,22250738585072011e-307',
+ '-123,123',
+ '-0,123',
+ '+0,123',
+ '0,123',
+ ',0',
+ '123.123',
+ '123٫123',
+ ' ',
+ '',
+ '.',
+ 'foo',
+ ],
+ });
+ test({
+ validator: 'isFloat',
+ args: [{
+ min: undefined,
+ max: undefined,
+ }],
+ valid: [
+ '123',
+ '123.',
+ '123.123',
+ '-767.767',
+ '+111.111',
+ ],
+ invalid: [
+ 'ab565',
+ '-,123',
+ '+,123',
+ '7866.t',
+ '123,123',
+ '123,',
+ ],
+ });
+ test({
+ validator: 'isFloat',
+ args: [{
+ gt: undefined,
+ lt: undefined,
+ }],
+ valid: [
+ '14.34343',
+ '11.1',
+ '456',
+ ],
+ invalid: [
+ 'ab565',
+ '-,123',
+ '+,123',
+ '7866.t',
+ ],
+ });
+ test({
+ validator: 'isFloat',
+ args: [{
+ locale: 'ar',
+ gt: null,
+ max: null,
+ }],
+ valid: [
+ '13324٫',
+ '12321',
+ '444٫83874',
+ ],
+ invalid: [
+ '55.55.55',
+ '1;23',
+ '+-123',
+ '1111111l1',
+ '3.3',
+ ],
+ });
+ test({
+ validator: 'isFloat',
+ args: [{
+ locale: 'ru-RU',
+ lt: null,
+ min: null,
+ }],
+ valid: [
+ '11231554,34343',
+ '11,1',
+ '456',
+ ',311',
+ ],
+ invalid: [
+ 'ab565',
+ '-.123',
+ '+.123',
+ '7866.t',
+ '22.3',
+ ],
+ });
+ });
+
+ it('should validate hexadecimal strings', () => {
+ test({
+ validator: 'isHexadecimal',
+ valid: [
+ 'deadBEEF',
+ 'ff0044',
+ '0xff0044',
+ '0XfF0044',
+ '0x0123456789abcDEF',
+ '0X0123456789abcDEF',
+ '0hfedCBA9876543210',
+ '0HfedCBA9876543210',
+ '0123456789abcDEF',
+ ],
+ invalid: [
+ 'abcdefg',
+ '',
+ '..',
+ '0xa2h',
+ '0xa20x',
+ '0x0123456789abcDEFq',
+ '0hfedCBA9876543210q',
+ '01234q56789abcDEF',
+ ],
+ });
+ });
+
+ it('should validate octal strings', () => {
+ test({
+ validator: 'isOctal',
+ valid: [
+ '076543210',
+ '0o01234567',
+ ],
+ invalid: [
+ 'abcdefg',
+ '012345678',
+ '012345670c',
+ '00c12345670c',
+ '',
+ '..',
+ ],
+ });
+ });
+
+ it('should validate hexadecimal color strings', () => {
+ test({
+ validator: 'isHexColor',
+ valid: [
+ '#ff0000ff',
+ '#ff0034',
+ '#CCCCCC',
+ '0f38',
+ 'fff',
+ '#f00',
+ ],
+ invalid: [
+ '#ff',
+ 'fff0a',
+ '#ff12FG',
+ ],
+ });
+ });
+
+ it('should validate HSL color strings', () => {
+ test({
+ validator: 'isHSL',
+ valid: [
+ 'hsl(360,0000000000100%,000000100%)',
+ 'hsl(000010, 00000000001%, 00000040%)',
+ 'HSL(00000,0000000000100%,000000100%)',
+ 'hsL(0, 0%, 0%)',
+ 'hSl( 360 , 100% , 100% )',
+ 'Hsl( 00150 , 000099% , 01% )',
+ 'hsl(01080, 03%, 4%)',
+ 'hsl(-540, 03%, 4%)',
+ 'hsla(+540, 03%, 4%)',
+ 'hsla(+540, 03%, 4%, 500)',
+ 'hsl(+540deg, 03%, 4%, 500)',
+ 'hsl(+540gRaD, 03%, 4%, 500)',
+ 'hsl(+540.01e-98rad, 03%, 4%, 500)',
+ 'hsl(-540.5turn, 03%, 4%, 500)',
+ 'hsl(+540, 03%, 4%, 500e-01)',
+ 'hsl(+540, 03%, 4%, 500e+80)',
+ 'hsl(4.71239rad, 60%, 70%)',
+ 'hsl(270deg, 60%, 70%)',
+ 'hsl(200, +.1%, 62%, 1)',
+ 'hsl(270 60% 70%)',
+ 'hsl(200, +.1e-9%, 62e10%, 1)',
+ 'hsl(.75turn, 60%, 70%)',
+ // 'hsl(200grad+.1%62%/1)', //supposed to pass, but need to handle delimiters
+ 'hsl(200grad +.1% 62% / 1)',
+ 'hsl(270, 60%, 50%, .15)',
+ 'hsl(270, 60%, 50%, 15%)',
+ 'hsl(270 60% 50% / .15)',
+ 'hsl(270 60% 50% / 15%)',
+ ],
+ invalid: [
+ 'hsl (360,0000000000100%,000000100%)',
+ 'hsl(0260, 100 %, 100%)',
+ 'hsl(0160, 100%, 100%, 100 %)',
+ 'hsl(-0160, 100%, 100a)',
+ 'hsl(-0160, 100%, 100)',
+ 'hsl(-0160 100%, 100%, )',
+ 'hsl(270 deg, 60%, 70%)',
+ 'hsl( deg, 60%, 70%)',
+ 'hsl(, 60%, 70%)',
+ 'hsl(3000deg, 70%)',
+ ],
+ });
+ });
+
+ it('should validate rgb color strings', () => {
+ test({
+ validator: 'isRgbColor',
+ valid: [
+ 'rgb(0,0,0)',
+ 'rgb(255,255,255)',
+ 'rgba(0,0,0,0)',
+ 'rgba(255,255,255,1)',
+ 'rgba(255,255,255,.1)',
+ 'rgba(255,255,255,0.1)',
+ 'rgba(255,255,255,.12)',
+ 'rgb(5%,5%,5%)',
+ 'rgba(5%,5%,5%,.3)',
+ 'rgba(5%,5%,5%,.32)',
+ ],
+ invalid: [
+ 'rgb(0,0,0,)',
+ 'rgb(0,0,)',
+ 'rgb(0,0,256)',
+ 'rgb()',
+ 'rgba(0,0,0)',
+ 'rgba(255,255,255,2)',
+ 'rgba(255,255,255,.123)',
+ 'rgba(255,255,256,0.1)',
+ 'rgb(4,4,5%)',
+ 'rgba(5%,5%,5%)',
+ 'rgba(3,3,3%,.3)',
+ 'rgba(5%,5%,5%,.321)',
+ 'rgb(101%,101%,101%)',
+ 'rgba(3%,3%,101%,0.3)',
+ 'rgb(101%,101%,101%) additional invalid string part',
+ 'rgba(3%,3%,101%,0.3) additional invalid string part',
+ 'r g b( 0, 251, 222 )',
+ 'r g ba( 0, 251, 222 )',
+ 'rg ba(0, 251, 22, 0.5)',
+ 'rgb( 255,255 ,255)',
+ 'rgba(255, 255, 255, 0.5)',
+ 'rgba(255, 255, 255, 0.5)',
+ 'rgb(5%, 5%, 5%)',
+ ],
+ });
+
+ // test empty options object
+ test({
+ validator: 'isRgbColor',
+ args: [{}],
+ valid: [
+ 'rgb(0,0,0)',
+ 'rgb(255,255,255)',
+ 'rgba(0,0,0,0)',
+ 'rgba(255,255,255,1)',
+ 'rgba(255,255,255,.1)',
+ 'rgba(255,255,255,.12)',
+ 'rgba(255,255,255,0.1)',
+ 'rgb(5%,5%,5%)',
+ 'rgba(5%,5%,5%,.3)',
+ ],
+ invalid: [
+ 'rgb(0,0,0,)',
+ 'rgb(0,0,)',
+ 'rgb(0,0,256)',
+ 'rgb()',
+ 'rgba(0,0,0)',
+ 'rgba(255,255,255,2)',
+ 'rgba(255,255,256,0.1)',
+ 'rgb(4,4,5%)',
+ 'rgba(5%,5%,5%)',
+ 'rgba(3,3,3%,.3)',
+ 'rgb(101%,101%,101%)',
+ 'rgba(3%,3%,101%,0.3)',
+ 'r g b( 0, 251, 222 )',
+ 'r g ba( 0, 251, 222 )',
+ 'rg ba(0, 251, 22, 0.5)',
+ 'rgb( 255,255 ,255)',
+ 'rgba(255, 255, 255, 0.5)',
+ 'rgba(255, 255, 255, 0.5)',
+ 'rgb(5%, 5%, 5%)',
+ ],
+ });
+ // test where includePercentValues is given as false
+ test({
+ validator: 'isRgbColor',
+ args: [false],
+ valid: [
+ 'rgb(5,5,5)',
+ 'rgba(5,5,5,.3)',
+ ],
+ invalid: [
+ 'rgb(4,4,5%)',
+ 'rgba(5%,5%,5%)',
+ 'r g b( 0, 251, 222 )',
+ 'r g ba( 0, 251, 222 )',
+ ],
+ });
+
+ // test where includePercentValues is given as false as part of options object
+ test({
+ validator: 'isRgbColor',
+ args: [{ includePercentValues: false }],
+ valid: [
+ 'rgb(5,5,5)',
+ 'rgba(5,5,5,.3)',
+ ],
+ invalid: [
+ 'rgb(4,4,5%)',
+ 'rgba(5%,5%,5%)',
+ 'r g b( 0, 251, 222 )',
+ 'rgba(255, 255, 255 ,0.2)',
+ 'r g ba( 0, 251, 222 )',
+ ],
+ });
+
+ // test where include percent is true explciitly
+ test({
+ validator: 'isRgbColor',
+ args: [true],
+ valid: [
+ 'rgb(5,5,5)',
+ 'rgba(5,5,5,.3)',
+ 'rgb(0,0,0)',
+ 'rgb(255,255,255)',
+ 'rgba(0,0,0,0)',
+ 'rgba(255,255,255,1)',
+ 'rgba(255,255,255,.1)',
+ 'rgba(255,255,255,.12)',
+ 'rgba(255,255,255,0.1)',
+ 'rgb(5%,5%,5%)',
+ 'rgba(5%,5%,5%,.3)',
+ 'rgb(5%,5%,5%)',
+ 'rgba(255,255,255,0.5)',
+ ],
+ invalid: [
+ 'rgba(255, 255, 255, 0.5)',
+ 'rgb(5%, 5%, 5%)',
+ 'rgb(4,4,5%)',
+ 'rgba(5%,5%,5%)',
+ 'r g b( 0, 251, 222 )',
+ 'r g ba( 0, 251, 222 )',
+ 'rgb(0,0,0,)',
+ 'rgb(0,0,)',
+ 'rgb(0,0,256)',
+ 'rgb()',
+ 'rgba(0,0,0)',
+ 'rgba(255,255,255,2)',
+ 'rgba(255,255,256,0.1)',
+ 'rgb(4,4,5%)',
+ 'rgba(5%,5%,5%)',
+ 'rgba(3,3,3%,.3)',
+ 'rgb(101%,101%,101%)',
+ 'rgba(3%,3%,101%,0.3)',
+ ],
+ });
+
+ // test where percent value is false and allowSpaces is true as part of options object
+ test({
+ validator: 'isRgbColor',
+ args: [{ includePercentValues: false, allowSpaces: true }],
+ valid: [
+ 'rgb(5,5,5)',
+ 'rgba(5,5,5,.3)',
+ 'rgba(255,255,255,0.2)',
+ 'rgba(255, 255, 255 ,0.2)',
+ ],
+ invalid: [
+ 'rgb(4,4,5%)',
+ 'rgba(5%,5%,5%)',
+ 'rgba(5% ,5%, 5%)',
+ 'r g b( 0, 251, 222 )',
+ 'r g ba( 0, 251, 222 )',
+ 'rgb(0,0,)',
+ 'rgb()',
+ 'rgb(4,4,5%)',
+ 'rgb(5%,5%,5%)',
+ 'rgba(3,3,3%,.3)',
+ 'rgb(101%, 101%, 101%)',
+ 'rgba(3%,3%,101%,0.3)',
+ ],
+
+ });
+
+ // test where both are true as part of options object
+ test({
+ validator: 'isRgbColor',
+ args: [{ includePercentValues: true, allowSpaces: true }],
+ valid: [
+ 'rgb( 5, 5, 5)',
+ 'rgba(5, 5, 5, .3)',
+ 'rgb(0, 0, 0)',
+ 'rgb(255, 255, 255)',
+ 'rgba(0, 0, 0, 0)',
+ 'rgba(255, 255, 255, 1)',
+ 'rgba(255, 255, 255, .1)',
+ 'rgba(255, 255, 255, 0.1)',
+ 'rgb(5% ,5% ,5%)',
+ 'rgba(5%,5%,5%, .3)',
+ ],
+ invalid: [
+ 'r g b( 0, 251, 222 )',
+ 'rgb(4,4,5%)',
+ 'rgb(101%,101%,101%)',
+
+ ],
+ });
+
+ // test where allowSpaces is false as part of options object
+ test({
+ validator: 'isRgbColor',
+ args: [{ includePercentValues: true, allowSpaces: false }],
+ valid: [
+ 'rgb(5,5,5)',
+ 'rgba(5,5,5,.3)',
+ 'rgb(0,0,0)',
+ 'rgb(255,255,255)',
+ 'rgba(0,0,0,0)',
+ 'rgba(255,255,255,1)',
+ 'rgba(255,255,255,.1)',
+ 'rgba(255,255,255,.12)',
+ 'rgba(255,255,255,0.1)',
+ 'rgb(5%,5%,5%)',
+ 'rgba(5%,5%,5%,.3)',
+
+ ],
+ invalid: [
+ 'rgb( 255,255 ,255)',
+ 'rgba(255, 255, 255, 0.5)',
+ 'rgb(5%, 5%, 5%)',
+ 'rgba(255, 255, 255, 0.5)',
+ 'rgb(4,4,5%)',
+ 'rgba(5%,5%,5%)',
+ 'r g b( 0, 251, 222 )',
+ 'r g ba( 0, 251, 222 )',
+ 'rgb(0,0,0,)',
+ 'rgb(0,0,)',
+ 'rgb(0,0,256)',
+ 'rgb()',
+ 'rgba(0,0,0)',
+ 'rgba(255,255,255,2)',
+ 'rgba(255,255,256,0.1)',
+ 'rgb(4,4,5%)',
+ 'rgba(5%,5%,5%)',
+ 'rgba(3,3,3%,.3)',
+ 'rgb(101%,101%,101%)',
+ 'rgba(3%,3%,101%,0.3)',
+ ],
+ });
+ });
+
+ it('should validate ISRC code strings', () => {
+ test({
+ validator: 'isISRC',
+ valid: [
+ 'USAT29900609',
+ 'GBAYE6800011',
+ 'USRC15705223',
+ 'USCA29500702',
+ ],
+ invalid: [
+ 'USAT2990060',
+ 'SRC15705223',
+ 'US-CA29500702',
+ 'USARC15705223',
+ ],
+ });
+ });
+
+ it('should validate md5 strings', () => {
+ test({
+ validator: 'isMD5',
+ valid: [
+ 'd94f3f016ae679c3008de268209132f2',
+ '751adbc511ccbe8edf23d486fa4581cd',
+ '88dae00e614d8f24cfd5a8b3f8002e93',
+ '0bf1c35032a71a14c2f719e5a14c1e96',
+ ],
+ invalid: [
+ 'KYT0bf1c35032a71a14c2f719e5a14c1',
+ 'q94375dj93458w34',
+ '39485729348',
+ '%&FHKJFvk',
+ ],
+ });
+ });
+
+ it('should validate hash strings', () => {
+ ['md5', 'md4', 'ripemd128', 'tiger128'].forEach((algorithm) => {
+ test({
+ validator: 'isHash',
+ args: [algorithm],
+ valid: [
+ 'd94f3f016ae679c3008de268209132f2',
+ '751adbc511ccbe8edf23d486fa4581cd',
+ '88dae00e614d8f24cfd5a8b3f8002e93',
+ '0bf1c35032a71a14c2f719e5a14c1e96',
+ 'd94f3F016Ae679C3008de268209132F2',
+ '88DAE00e614d8f24cfd5a8b3f8002E93',
+ ],
+ invalid: [
+ 'q94375dj93458w34',
+ '39485729348',
+ '%&FHKJFvk',
+ 'KYT0bf1c35032a71a14c2f719e5a1',
+ ],
+ });
+ });
+
+ ['crc32', 'crc32b'].forEach((algorithm) => {
+ test({
+ validator: 'isHash',
+ args: [algorithm],
+ valid: [
+ 'd94f3f01',
+ '751adbc5',
+ '88dae00e',
+ '0bf1c350',
+ '88DAE00e',
+ '751aDBc5',
+ ],
+ invalid: [
+ 'KYT0bf1c35032a71a14c2f719e5a14c1',
+ 'q94375dj93458w34',
+ 'q943',
+ '39485729348',
+ '%&FHKJFvk',
+ ],
+ });
+ });
+
+ ['sha1', 'tiger160', 'ripemd160'].forEach((algorithm) => {
+ test({
+ validator: 'isHash',
+ args: [algorithm],
+ valid: [
+ '3ca25ae354e192b26879f651a51d92aa8a34d8d3',
+ 'aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d',
+ 'beb8c3f30da46be179b8df5f5ecb5e4b10508230',
+ 'efd5d3b190e893ed317f38da2420d63b7ae0d5ed',
+ 'AAF4c61ddCC5e8a2dabede0f3b482cd9AEA9434D',
+ '3ca25AE354e192b26879f651A51d92aa8a34d8D3',
+ ],
+ invalid: [
+ 'KYT0bf1c35032a71a14c2f719e5a14c1',
+ 'KYT0bf1c35032a71a14c2f719e5a14c1dsjkjkjkjkkjk',
+ 'q94375dj93458w34',
+ '39485729348',
+ '%&FHKJFvk',
+ ],
+ });
+ });
+
+ test({
+ validator: 'isHash',
+ args: ['sha256'],
+ valid: [
+ '2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824',
+ '1d996e033d612d9af2b44b70061ee0e868bfd14c2dd90b129e1edeb7953e7985',
+ '80f70bfeaed5886e33536bcfa8c05c60afef5a0e48f699a7912d5e399cdcc441',
+ '579282cfb65ca1f109b78536effaf621b853c9f7079664a3fbe2b519f435898c',
+ '2CF24dba5FB0a30e26E83b2AC5b9E29E1b161e5C1fa7425E73043362938b9824',
+ '80F70bFEAed5886e33536bcfa8c05c60aFEF5a0e48f699a7912d5e399cdCC441',
+ ],
+ invalid: [
+ 'KYT0bf1c35032a71a14c2f719e5a14c1',
+ 'KYT0bf1c35032a71a14c2f719e5a14c1dsjkjkjkjkkjk',
+ 'q94375dj93458w34',
+ '39485729348',
+ '%&FHKJFvk',
+ ],
+ });
+ test({
+ validator: 'isHash',
+ args: ['sha384'],
+ valid: [
+ '3fed1f814d28dc5d63e313f8a601ecc4836d1662a19365cbdcf6870f6b56388850b58043f7ebf2418abb8f39c3a42e31',
+ 'b330f4e575db6e73500bd3b805db1a84b5a034e5d21f0041d91eec85af1dfcb13e40bb1c4d36a72487e048ac6af74b58',
+ 'bf547c3fc5841a377eb1519c2890344dbab15c40ae4150b4b34443d2212e5b04aa9d58865bf03d8ae27840fef430b891',
+ 'fc09a3d11368386530f985dacddd026ae1e44e0e297c805c3429d50744e6237eb4417c20ffca8807b071823af13a3f65',
+ '3fed1f814d28dc5d63e313f8A601ecc4836d1662a19365CBDCf6870f6b56388850b58043f7ebf2418abb8f39c3a42e31',
+ 'b330f4E575db6e73500bd3b805db1a84b5a034e5d21f0041d91EEC85af1dfcb13e40bb1c4d36a72487e048ac6af74b58',
+ ],
+ invalid: [
+ 'KYT0bf1c35032a71a14c2f719e5a14c1',
+ 'KYT0bf1c35032a71a14c2f719e5a14c1dsjkjkjkjkkjk',
+ 'q94375dj93458w34',
+ '39485729348',
+ '%&FHKJFvk',
+ ],
+ });
+ test({
+ validator: 'isHash',
+ args: ['sha512'],
+ valid: [
+ '9b71d224bd62f3785d96d46ad3ea3d73319bfbc2890caadae2dff72519673ca72323c3d99ba5c11d7c7acc6e14b8c5da0c4663475c2e5c3adef46f73bcdec043',
+ '83c586381bf5ba94c8d9ba8b6b92beb0997d76c257708742a6c26d1b7cbb9269af92d527419d5b8475f2bb6686d2f92a6649b7f174c1d8306eb335e585ab5049',
+ '45bc5fa8cb45ee408c04b6269e9f1e1c17090c5ce26ffeeda2af097735b29953ce547e40ff3ad0d120e5361cc5f9cee35ea91ecd4077f3f589b4d439168f91b9',
+ '432ac3d29e4f18c7f604f7c3c96369a6c5c61fc09bf77880548239baffd61636d42ed374f41c261e424d20d98e320e812a6d52865be059745fdb2cb20acff0ab',
+ '9B71D224bd62f3785D96d46ad3ea3d73319bFBC2890CAAdae2dff72519673CA72323C3d99ba5c11d7c7ACC6e14b8c5DA0c4663475c2E5c3adef46f73bcDEC043',
+ '432AC3d29E4f18c7F604f7c3c96369A6C5c61fC09Bf77880548239baffd61636d42ed374f41c261e424d20d98e320e812a6d52865be059745fdb2cb20acff0ab',
+ ],
+ invalid: [
+ 'KYT0bf1c35032a71a14c2f719e5a14c1',
+ 'KYT0bf1c35032a71a14c2f719e5a14c1dsjkjkjkjkkjk',
+ 'q94375dj93458w34',
+ '39485729348',
+ '%&FHKJFvk',
+ ],
+ });
+ test({
+ validator: 'isHash',
+ args: ['tiger192'],
+ valid: [
+ '6281a1f098c5e7290927ed09150d43ff3990a0fe1a48267c',
+ '56268f7bc269cf1bc83d3ce42e07a85632394737918f4760',
+ '46fc0125a148788a3ac1d649566fc04eb84a746f1a6e4fa7',
+ '7731ea1621ae99ea3197b94583d034fdbaa4dce31a67404a',
+ '6281A1f098c5e7290927ed09150d43ff3990a0fe1a48267C',
+ '46FC0125a148788a3AC1d649566fc04eb84A746f1a6E4fa7',
+ ],
+ invalid: [
+ 'KYT0bf1c35032a71a14c2f719e5a14c1',
+ 'KYT0bf1c35032a71a14c2f719e5a14c1dsjkjkjkjkkjk',
+ 'q94375dj93458w34',
+ '39485729348',
+ '%&FHKJFvk',
+ ],
+ });
+ });
+ it('should validate JWT tokens', () => {
+ test({
+ validator: 'isJWT',
+ valid: [
+ 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2dnZWRJbkFzIjoiYWRtaW4iLCJpYXQiOjE0MjI3Nzk2Mzh9.gzSraSYS8EXBxLN_oWnFSRgCzcmJmMjLiuyu5CSpyHI',
+ 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb3JlbSI6Imlwc3VtIn0.ymiJSsMJXR6tMSr8G9usjQ15_8hKPDv_CArLhxw28MI',
+ 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkb2xvciI6InNpdCIsImFtZXQiOlsibG9yZW0iLCJpcHN1bSJdfQ.rRpe04zbWbbJjwM43VnHzAboDzszJtGrNsUxaqQ-GQ8',
+ 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqb2huIjp7ImFnZSI6MjUsImhlaWdodCI6MTg1fSwiamFrZSI6eyJhZ2UiOjMwLCJoZWlnaHQiOjI3MH19.YRLPARDmhGMC3BBk_OhtwwK21PIkVCqQe8ncIRPKo-E',
+ ],
+ invalid: [
+ 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9',
+ 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NSIsIm5hbWUiOiJKb2huIERvZSIsImlhdCI6MTUxNjIzOTAyMn0',
+ 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NSIsIm5hbWUiOiJKb2huIERvZSIsImlhdCI6MTYxNjY1Mzg3Mn0.eyJpc3MiOiJodHRwczovL2V4YW1wbGUuY29tIiwiaWF0IjoxNjE2NjUzODcyLCJleHAiOjE2MTY2NTM4ODJ9.a1jLRQkO5TV5y5ERcaPAiM9Xm2gBdRjKrrCpHkGr_8M',
+ '$Zs.ewu.su84',
+ 'ks64$S/9.dy$§kz.3sd73b',
+ ],
+ error: [
+ [],
+ {},
+ null,
+ undefined,
+ ],
+ });
+ });
+
+ it('should validate null strings', () => {
+ test({
+ validator: 'isEmpty',
+ valid: [
+ '',
+ ],
+ invalid: [
+ ' ',
+ 'foo',
+ '3',
+ ],
+ });
+ test({
+ validator: 'isEmpty',
+ args: [{ ignore_whitespace: false }],
+ valid: [
+ '',
+ ],
+ invalid: [
+ ' ',
+ 'foo',
+ '3',
+ ],
+ });
+ test({
+ validator: 'isEmpty',
+ args: [{ ignore_whitespace: true }],
+ valid: [
+ '',
+ ' ',
+ ],
+ invalid: [
+ 'foo',
+ '3',
+ ],
+ });
+ });
+
+ it('should validate strings against an expected value', () => {
+ test({
+ validator: 'equals', args: ['abc'], valid: ['abc'], invalid: ['Abc', '123'],
+ });
+ });
+
+ it('should validate strings contain another string', () => {
+ test({
+ validator: 'contains',
+ args: ['foo'],
+ valid: ['foo', 'foobar', 'bazfoo'],
+ invalid: ['bar', 'fobar'],
+ });
+
+ test({
+ validator: 'contains',
+ args: ['foo', {
+ ignoreCase: true,
+ }],
+ valid: ['Foo', 'FOObar', 'BAZfoo'],
+ invalid: ['bar', 'fobar', 'baxoof'],
+ });
+
+ test({
+ validator: 'contains',
+ args: ['foo', {
+ minOccurrences: 2,
+ }],
+ valid: ['foofoofoo', '12foo124foo', 'fofooofoooofoooo', 'foo1foo'],
+ invalid: ['foo', 'foobar', 'Fooofoo', 'foofo'],
+ });
+ });
+
+ it('should validate strings against a pattern', () => {
+ test({
+ validator: 'matches',
+ args: [/abc/],
+ valid: ['abc', 'abcdef', '123abc'],
+ invalid: ['acb', 'Abc'],
+ });
+ test({
+ validator: 'matches',
+ args: ['abc'],
+ valid: ['abc', 'abcdef', '123abc'],
+ invalid: ['acb', 'Abc'],
+ });
+ test({
+ validator: 'matches',
+ args: ['abc', 'i'],
+ valid: ['abc', 'abcdef', '123abc', 'AbC'],
+ invalid: ['acb'],
+ });
+ });
+
+
+ it('should validate isLocale codes', () => {
+ test({
+ validator: 'isLocale',
+ valid: [
+ 'uz_Latn_UZ',
+ 'en',
+ 'gsw',
+ 'en-US',
+ 'es_ES',
+ 'es-419',
+ 'sw_KE',
+ 'am_ET',
+ 'zh-CHS',
+ 'ca_ES_VALENCIA',
+ 'en_US_POSIX',
+ 'hak-CN',
+ 'zh-Hant',
+ 'zh-Hans',
+ 'sr-Cyrl',
+ 'sr-Latn',
+ 'zh-cmn-Hans-CN',
+ 'cmn-Hans-CN',
+ 'zh-yue-HK',
+ 'yue-HK',
+ 'zh-Hans-CN',
+ 'sr-Latn-RS',
+ 'sl-rozaj',
+ 'sl-rozaj-biske',
+ 'sl-nedis',
+ 'de-CH-1901',
+ 'sl-IT-nedis',
+ 'hy-Latn-IT-arevela',
+ 'i-enochian',
+ 'en-scotland-fonipa',
+ 'sl-IT-rozaj-biske-1994',
+ 'de-CH-x-phonebk',
+ 'az-Arab-x-AZE-derbend',
+ 'x-whatever',
+ 'qaa-Qaaa-QM-x-southern',
+ 'de-Qaaa',
+ 'sr-Latn-QM',
+ 'sr-Qaaa-RS',
+ 'en-US-u-islamcal',
+ 'zh-CN-a-myext-x-private',
+ 'en-a-myext-b-another',
+ ],
+ invalid: [
+ 'lo_POP',
+ '12',
+ '12_DD',
+ 'de-419-DE',
+ 'a-DE',
+ ],
+ });
+ });
+
+ it('should validate strings by byte length (deprecated api)', () => {
+ test({
+ validator: 'isByteLength',
+ args: [2],
+ valid: ['abc', 'de', 'abcd', 'gmail'],
+ invalid: ['', 'a'],
+ });
+ test({
+ validator: 'isByteLength',
+ args: [2, 3],
+ valid: ['abc', 'de', 'g'],
+ invalid: ['', 'a', 'abcd', 'gm'],
+ });
+ test({
+ validator: 'isByteLength',
+ args: [0, 0],
+ valid: [''],
+ invalid: ['g', 'a'],
+ });
+ });
+
+
+ it('should validate strings by byte length', () => {
+ test({
+ validator: 'isByteLength',
+ args: [{ min: 2 }],
+ valid: ['abc', 'de', 'abcd', 'gmail'],
+ invalid: ['', 'a'],
+ });
+ test({
+ validator: 'isByteLength',
+ args: [{ min: 2, max: 3 }],
+ valid: ['abc', 'de', 'g'],
+ invalid: ['', 'a', 'abcd', 'gm'],
+ });
+ test({
+ validator: 'isByteLength',
+ args: [{ max: 3 }],
+ valid: ['abc', 'de', 'g', 'a', ''],
+ invalid: ['abcd', 'gm'],
+ });
+ test({
+ validator: 'isByteLength',
+ args: [{ max: 0 }],
+ valid: [''],
+ invalid: ['g', 'a'],
+ });
+ });
+
+ it('should validate ULIDs', () => {
+ test({
+ validator: 'isULID',
+ valid: [
+ '01HBGW8CWQ5Q6DTT7XP89VV4KT',
+ '01HBGW8CWR8MZQMBG6FA2QHMDD',
+ '01HBGW8CWS3MEEK12Y9G7SVW4V',
+ '01hbgw8cws1tq2njavy9amb0wx',
+ '01HBGW8cwS43H4jkQ0A4ZRJ7QV',
+ ],
+ invalid: [
+ '',
+ '01HBGW-CWS3MEEK1#Y9G7SVW4V',
+ '91HBGW8CWS3MEEK12Y9G7SVW4V',
+ '81HBGW8CWS3MEEK12Y9G7SVW4V',
+ '934859',
+ '01HBGW8CWS3MEEK12Y9G7SVW4VXXX',
+ '01UBGW8IWS3MOEK12Y9G7SVW4V',
+ '01HBGW8CuS43H4JKQ0A4ZRJ7QV',
+ ],
+ });
+ });
+
+ it('should validate UUIDs', () => {
+ test({
+ validator: 'isUUID',
+ valid: [
+ '9deb20fe-a6e0-355c-81ea-288b009e4f6d',
+ 'A987FBC9-4BED-4078-8F07-9141BA07C9F3',
+ 'A987FBC9-4BED-5078-AF07-9141BA07C9F3',
+ 'A987FBC9-4BED-6078-AF07-9141BA07C9F3',
+ '018C544A-D384-7000-BB74-3B1738ABE43C',
+ 'A987FBC9-4BED-8078-AF07-9141BA07C9F3',
+ ],
+ invalid: [
+ '',
+ 'xxxA987FBC9-4BED-3078-CF07-9141BA07C9F3',
+ 'A987FBC9-4BED-3078-CF07-9141BA07C9F3',
+ 'A987FBC9-4BED-3078-CF07-9141BA07C9F3xxx',
+ 'A987FBC94BED3078CF079141BA07C9F3',
+ '934859',
+ '987FBC9-4BED-3078-CF07A-9141BA07C9F3',
+ 'AAAAAAAA-1111-1111-AAAG-111111111111',
+ ],
+ });
+ test({
+ validator: 'isUUID',
+ args: [undefined],
+ valid: [
+ '9deb20fe-a6e0-355c-81ea-288b009e4f6d',
+ 'A117FBC9-4BED-5078-AF07-9141BA07C9F3',
+ '018C544A-D384-7000-BB74-3B1738ABE43C',
+ ],
+ invalid: [
+ '',
+ 'A117FBC9-4BED-3078-CF07-9141BA07C9F3',
+ 'xxxA987FBC9-4BED-3078-CF07-9141BA07C9F3',
+ 'A987FBC94BED3078CF079141BA07C9F3',
+ 'A11AAAAA-1111-1111-AAAG-111111111111',
+ ],
+ });
+ test({
+ validator: 'isUUID',
+ args: [null],
+ valid: [
+ 'A127FBC9-4BED-3078-AF07-9141BA07C9F3',
+ '018C544A-D384-7000-BB74-3B1738ABE43C',
+ ],
+ invalid: [
+ '',
+ 'xxxA987FBC9-4BED-3078-CF07-9141BA07C9F3',
+ 'A127FBC9-4BED-3078-CF07-9141BA07C9F3',
+ 'A127FBC9-4BED-3078-CF07-9141BA07C9F3xxx',
+ '912859',
+ 'A12AAAAA-1111-1111-AAAG-111111111111',
+ ],
+ });
+ test({
+ validator: 'isUUID',
+ args: [1],
+ valid: [
+ 'E034B584-7D89-11E9-9669-1AECF481A97B',
+ ],
+ invalid: [
+ 'xxxA987FBC9-4BED-3078-CF07-9141BA07C9F3',
+ 'AAAAAAAA-1111-2222-AAAG',
+ 'AAAAAAAA-1111-2222-AAAG-111111111111',
+ 'A987FBC9-4BED-4078-8F07-9141BA07C9F3',
+ 'A987FBC9-4BED-5078-AF07-9141BA07C9F3',
+ '018C544A-D384-7000-BB74-3B1738ABE43C',
+ ],
+ });
+ test({
+ validator: 'isUUID',
+ args: [2],
+ valid: [
+ 'A987FBC9-4BED-2078-AF07-9141BA07C9F3',
+ ],
+ invalid: [
+ '',
+ 'xxxA987FBC9-4BED-3078-CF07-9141BA07C9F3',
+ '11111',
+ 'AAAAAAAA-1111-1111-AAAG-111111111111',
+ 'A987FBC9-4BED-2078-CF07-9141BA07C9F3',
+ 'A987FBC9-4BED-4078-8F07-9141BA07C9F3',
+ 'A987FBC9-4BED-5078-AF07-9141BA07C9F3',
+ '018C544A-D384-7000-BB74-3B1738ABE43C',
+ ],
+ });
+ test({
+ validator: 'isUUID',
+ args: [3],
+ valid: [
+ '9deb20fe-a6e0-355c-81ea-288b009e4f6d',
+ ],
+ invalid: [
+ '',
+ 'A987FBC9-4BED-3078-CF07-9141BA07C9F3',
+ 'xxxA987FBC9-4BED-3078-CF07-9141BA07C9F3',
+ '934859',
+ 'AAAAAAAA-1111-1111-AAAG-111111111111',
+ 'A987FBC9-4BED-4078-8F07-9141BA07C9F3',
+ 'A987FBC9-4BED-5078-AF07-9141BA07C9F3',
+ '018C544A-D384-7000-BB74-3B1738ABE43C',
+ ],
+ });
+ test({
+ validator: 'isUUID',
+ args: [4],
+ valid: [
+ '713ae7e3-cb32-45f9-adcb-7c4fa86b90c1',
+ '625e63f3-58f5-40b7-83a1-a72ad31acffb',
+ '57b73598-8764-4ad0-a76a-679bb6640eb1',
+ '9c858901-8a57-4791-81fe-4c455b099bc9',
+ ],
+ invalid: [
+ '',
+ 'xxxA987FBC9-4BED-3078-CF07-9141BA07C9F3',
+ '934859',
+ 'AAAAAAAA-1111-1111-AAAG-111111111111',
+ 'A987FBC9-4BED-5078-AF07-9141BA07C9F3',
+ 'A987FBC9-4BED-3078-CF07-9141BA07C9F3',
+ '018C544A-D384-7000-BB74-3B1738ABE43C',
+ ],
+ });
+ test({
+ validator: 'isUUID',
+ args: [5],
+ valid: [
+ '987FBC97-4BED-5078-AF07-9141BA07C9F3',
+ '987FBC97-4BED-5078-BF07-9141BA07C9F3',
+ '987FBC97-4BED-5078-8F07-9141BA07C9F3',
+ '987FBC97-4BED-5078-9F07-9141BA07C9F3',
+ ],
+ invalid: [
+ '',
+ 'xxxA987FBC9-4BED-3078-CF07-9141BA07C9F3',
+ '934859',
+ 'AAAAAAAA-1111-1111-AAAG-111111111111',
+ '9c858901-8a57-4791-81fe-4c455b099bc9',
+ 'A987FBC9-4BED-3078-CF07-9141BA07C9F3',
+ '018C544A-D384-7000-BB74-3B1738ABE43C',
+ ],
+ });
+ test({
+ validator: 'isUUID',
+ args: [6],
+ valid: [
+ '1ef29908-cde1-69d0-be16-bfc8518a95f0',
+ ],
+ invalid: [
+ '987FBC97-4BED-1078-AF07-9141BA07C9F3',
+ '987FBC97-4BED-2078-AF07-9141BA07C9F3',
+ '987FBC97-4BED-3078-AF07-9141BA07C9F3',
+ '987FBC97-4BED-4078-AF07-9141BA07C9F3',
+ '987FBC97-4BED-5078-AF07-9141BA07C9F3',
+ '018C544A-D384-7000-BB74-3B1738ABE43C',
+ '987FBC97-4BED-8078-AF07-9141BA07C9F3',
+ ],
+ });
+ test({
+ validator: 'isUUID',
+ args: [7],
+ valid: [
+ '018C544A-D384-7000-BB74-3B1738ABE43C',
+ ],
+ invalid: [
+ '',
+ 'xxxA987FBC9-4BED-3078-CF07-9141BA07C9F3',
+ '934859',
+ 'AAAAAAAA-1111-1111-AAAG-111111111111',
+ 'A987FBC9-4BED-5078-AF07-9141BA07C9F3',
+ 'A987FBC9-4BED-3078-CF07-9141BA07C9F3',
+ 'A987FBC9-4BED-6078-AF07-9141BA07C9F3',
+ 'A987FBC9-4BED-8078-AF07-9141BA07C9F3',
+ '713ae7e3-cb32-45f9-adcb-7c4fa86b90c1',
+ '625e63f3-58f5-40b7-83a1-a72ad31acffb',
+ '57b73598-8764-4ad0-a76a-679bb6640eb1',
+ '9c858901-8a57-4791-81fe-4c455b099bc9',
+ ],
+ });
+ test({
+ validator: 'isUUID',
+ args: [8],
+ valid: [
+ '018C544A-D384-8000-BB74-3B1738ABE43C',
+ ],
+ invalid: [
+ '',
+ 'xxxA987FBC9-4BED-3078-CF07-9141BA07C9F3',
+ '934859',
+ 'AAAAAAAA-1111-1111-AAAG-111111111111',
+ 'A987FBC9-4BED-5078-AF07-9141BA07C9F3',
+ 'A987FBC9-4BED-3078-CF07-9141BA07C9F3',
+ 'A987FBC9-4BED-6078-AF07-9141BA07C9F3',
+ 'A987FBC9-4BED-7078-AF07-9141BA07C9F3',
+ '713ae7e3-cb32-45f9-adcb-7c4fa86b90c1',
+ '625e63f3-58f5-40b7-83a1-a72ad31acffb',
+ '57b73598-8764-4ad0-a76a-679bb6640eb1',
+ '9c858901-8a57-4791-81fe-4c455b099bc9',
+ ],
+ });
+ test({
+ validator: 'isUUID',
+ args: ['nil'],
+ valid: [
+ '00000000-0000-0000-0000-000000000000',
+ ],
+ invalid: [
+ '',
+ 'xxxA987FBC9-4BED-3078-CF07-9141BA07C9F3',
+ 'A987FBC9-4BED-3078-CF07-9141BA07C9F3',
+ 'A987FBC9-4BED-3078-CF07-9141BA07C9F3xxx',
+ 'A987FBC94BED3078CF079141BA07C9F3',
+ '934859',
+ '987FBC9-4BED-3078-CF07A-9141BA07C9F3',
+ 'AAAAAAAA-1111-1111-AAAG-111111111111',
+ '9deb20fe-a6e0-355c-81ea-288b009e4f6d',
+ 'A987FBC9-4BED-4078-8F07-9141BA07C9F3',
+ 'A987FBC9-4BED-5078-AF07-9141BA07C9F3',
+ 'A987FBC9-4BED-6078-AF07-9141BA07C9F3',
+ '018C544A-D384-7000-BB74-3B1738ABE43C',
+ 'A987FBC9-4BED-8078-AF07-9141BA07C9F3',
+ 'ffffffff-ffff-ffff-ffff-ffffffffffff',
+ 'FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF',
+ ],
+ });
+ test({
+ validator: 'isUUID',
+ args: ['max'],
+ valid: [
+ 'ffffffff-ffff-ffff-ffff-ffffffffffff',
+ 'FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF',
+ ],
+ invalid: [
+ '',
+ 'xxxA987FBC9-4BED-3078-CF07-9141BA07C9F3',
+ 'A987FBC9-4BED-3078-CF07-9141BA07C9F3',
+ 'A987FBC9-4BED-3078-CF07-9141BA07C9F3xxx',
+ 'A987FBC94BED3078CF079141BA07C9F3',
+ '934859',
+ '987FBC9-4BED-3078-CF07A-9141BA07C9F3',
+ 'AAAAAAAA-1111-1111-AAAG-111111111111',
+ '9deb20fe-a6e0-355c-81ea-288b009e4f6d',
+ 'A987FBC9-4BED-4078-8F07-9141BA07C9F3',
+ 'A987FBC9-4BED-5078-AF07-9141BA07C9F3',
+ 'A987FBC9-4BED-6078-AF07-9141BA07C9F3',
+ '018C544A-D384-7000-BB74-3B1738ABE43C',
+ 'A987FBC9-4BED-8078-AF07-9141BA07C9F3',
+ '00000000-0000-0000-0000-000000000000',
+ ],
+ });
+ test({
+ validator: 'isUUID',
+ args: ['loose'],
+ valid: [
+ '9deb20fe-a6e0-355c-81ea-288b009e4f6d',
+ 'A987FBC9-4BED-3078-CF07-9141BA07C9F3',
+ 'A987FBC9-4BED-4078-8F07-9141BA07C9F3',
+ 'A987FBC9-4BED-5078-AF07-9141BA07C9F3',
+ 'A987FBC9-4BED-6078-AF07-9141BA07C9F3',
+ '018C544A-D384-7000-BB74-3B1738ABE43C',
+ 'A987FBC9-4BED-8078-AF07-9141BA07C9F3',
+ 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa',
+ 'AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA',
+ 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee',
+ 'EEEEEEEE-EEEE-EEEE-EEEE-EEEEEEEEEEEE',
+ '99999999-9999-9999-9999-999999999999',
+ ],
+ invalid: [
+ '',
+ 'xxxA987FBC9-4BED-3078-CF07-9141BA07C9F3',
+ 'A987FBC9-4BED-3078-CF07-9141BA07C9F3xxx',
+ 'A987FBC94BED3078CF079141BA07C9F3',
+ '987FBC9-4BED-3078-CF07A-9141BA07C9F3',
+ '934859',
+ 'AAAAAAAA-1111-1111-AAAG-111111111111',
+ ],
+ });
+ test({
+ validator: 'isUUID',
+ args: ['all'],
+ valid: [
+ '9deb20fe-a6e0-355c-81ea-288b009e4f6d',
+ 'A987FBC9-4BED-4078-8F07-9141BA07C9F3',
+ 'A987FBC9-4BED-5078-AF07-9141BA07C9F3',
+ 'A987FBC9-4BED-6078-AF07-9141BA07C9F3',
+ '018C544A-D384-7000-BB74-3B1738ABE43C',
+ 'A987FBC9-4BED-8078-AF07-9141BA07C9F3',
+ '00000000-0000-0000-0000-000000000000',
+ 'ffffffff-ffff-ffff-ffff-ffffffffffff',
+ 'FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF',
+ ],
+ invalid: [
+ '',
+ 'xxxA987FBC9-4BED-3078-CF07-9141BA07C9F3',
+ 'A987FBC9-4BED-3078-CF07-9141BA07C9F3',
+ 'A987FBC9-4BED-3078-CF07-9141BA07C9F3xxx',
+ 'A987FBC94BED3078CF079141BA07C9F3',
+ '934859',
+ '987FBC9-4BED-3078-CF07A-9141BA07C9F3',
+ 'AAAAAAAA-1111-1111-AAAG-111111111111',
+ ],
+ });
+ test({
+ validator: 'isUUID',
+ args: ['invalid'],
+ valid: [],
+ invalid: [
+ '',
+ 'xxxA987FBC9-4BED-3078-CF07-9141BA07C9F3',
+ 'A987FBC9-4BED-3078-CF07-9141BA07C9F3',
+ 'A987FBC9-4BED-3078-CF07-9141BA07C9F3xxx',
+ 'A987FBC94BED3078CF079141BA07C9F3',
+ '934859',
+ '987FBC9-4BED-3078-CF07A-9141BA07C9F3',
+ 'AAAAAAAA-1111-1111-AAAG-111111111111',
+ '9deb20fe-a6e0-355c-81ea-288b009e4f6d',
+ 'A987FBC9-4BED-4078-8F07-9141BA07C9F3',
+ 'A987FBC9-4BED-5078-AF07-9141BA07C9F3',
+ 'A987FBC9-4BED-6078-AF07-9141BA07C9F3',
+ '018C544A-D384-7000-BB74-3B1738ABE43C',
+ 'A987FBC9-4BED-8078-AF07-9141BA07C9F3',
+ '00000000-0000-0000-0000-000000000000',
+ 'ffffffff-ffff-ffff-ffff-ffffffffffff',
+ 'FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF',
+ ],
+ });
+ });
+
+ it('should validate a string that is in another string or array', () => {
+ test({
+ validator: 'isIn',
+ args: ['foobar'],
+ valid: ['foo', 'bar', 'foobar', ''],
+ invalid: ['foobarbaz', 'barfoo'],
+ });
+ test({
+ validator: 'isIn',
+ args: [['foo', 'bar']],
+ valid: ['foo', 'bar'],
+ invalid: ['foobar', 'barfoo', ''],
+ });
+ test({
+ validator: 'isIn',
+ args: [['1', '2', '3']],
+ valid: ['1', '2', '3'],
+ invalid: ['4', ''],
+ });
+ test({
+ validator: 'isIn',
+ args: [['1', '2', '3', { foo: 'bar' }, () => 5, { toString: 'test' }]],
+ valid: ['1', '2', '3', ''],
+ invalid: ['4'],
+ });
+ test({ validator: 'isIn', invalid: ['foo', ''] });
+ });
+
+ it('should validate a string that is in another object', () => {
+ test({
+ validator: 'isIn',
+ args: [{ foo: 1, bar: 2, foobar: 3 }],
+ valid: ['foo', 'bar', 'foobar'],
+ invalid: ['foobarbaz', 'barfoo', ''],
+ });
+ test({
+ validator: 'isIn',
+ args: [{ 1: 3, 2: 0, 3: 1 }],
+ valid: ['1', '2', '3'],
+ invalid: ['4', ''],
+ });
+ });
+
+ it('should validate ABA routing number', () => {
+ test({
+ validator: 'isAbaRouting',
+ valid: [
+ '322070381',
+ '011103093',
+ '263170175',
+ '124303065',
+ ],
+ invalid: [
+ '426317017',
+ '789456124',
+ '603558459',
+ 'qwerty',
+ '12430306',
+ '382070381',
+ ],
+ });
+ });
+
+ it('should validate IBAN', () => {
+ test({
+ validator: 'isIBAN',
+ valid: [
+ 'SC52BAHL01031234567890123456USD',
+ 'LC14BOSL123456789012345678901234',
+ 'MT31MALT01100000000000000000123',
+ 'SV43ACAT00000000000000123123',
+ 'EG800002000156789012345180002',
+ 'BE71 0961 2345 6769',
+ 'FR76 3000 6000 0112 3456 7890 189',
+ 'DE91 1000 0000 0123 4567 89',
+ 'GR96 0810 0010 0000 0123 4567 890',
+ 'RO09 BCYP 0000 0012 3456 7890',
+ 'SA44 2000 0001 2345 6789 1234',
+ 'ES79 2100 0813 6101 2345 6789',
+ 'CH56 0483 5012 3456 7800 9',
+ 'GB98 MIDL 0700 9312 3456 78',
+ 'IL170108000000012612345',
+ 'IT60X0542811101000000123456',
+ 'JO71CBJO0000000000001234567890',
+ 'TR320010009999901234567890',
+ 'BR1500000000000010932840814P2',
+ 'LB92000700000000123123456123',
+ 'IR200170000000339545727003',
+ 'MZ97123412341234123412341',
+ 'MA64011519000001205000534921',
+ 'VG96VPVG0000012345678901',
+ 'DZ580002100001113000000570',
+ 'IE29AIBK93115212345678',
+ 'PS92PALS000000000400123456702',
+ 'PS92PALS00000000040012345670O',
+ ],
+ invalid: [
+ 'XX22YYY1234567890123',
+ 'FR14 2004 1010 0505 0001 3',
+ 'FR7630006000011234567890189@',
+ 'FR7630006000011234567890189😅',
+ 'FR763000600001123456!!🤨7890189@',
+ 'VG46H07Y0223060094359858',
+ 'IE95TE8270900834048660',
+ 'PS072435171802145240705922007',
+ ],
+ });
+ test({
+ validator: 'isIBAN',
+ args: [{ whitelist: ['DK', 'GB'] }],
+ valid: [
+ 'DK5000400440116243',
+ 'GB29NWBK60161331926819',
+ ],
+ invalid: [
+ 'BE71 0961 2345 6769',
+ 'FR76 3000 6000 0112 3456 7890 189',
+ 'DE91 1000 0000 0123 4567 89',
+ 'GR96 0810 0010 0000 0123 4567 890',
+ 'RO09 BCYP 0000 0012 3456 7890',
+ 'SA44 2000 0001 2345 6789 1234',
+ 'ES79 2100 0813 6101 2345 6789',
+ 'XX22YYY1234567890123',
+ 'FR14 2004 1010 0505 0001 3',
+ 'FR7630006000011234567890189@',
+ 'FR7630006000011234567890189😅',
+ 'FR763000600001123456!!🤨7890189@',
+ ],
+ });
+ test({
+ validator: 'isIBAN',
+ args: [{ whitelist: ['XX', 'AA'] }],
+ invalid: [
+ 'DK5000400440116243',
+ 'GB29NWBK60161331926819',
+ 'BE71 0961 2345 6769',
+ 'FR76 3000 6000 0112 3456 7890 189',
+ 'DE91 1000 0000 0123 4567 89',
+ 'GR96 0810 0010 0000 0123 4567 890',
+ 'RO09 BCYP 0000 0012 3456 7890',
+ 'SA44 2000 0001 2345 6789 1234',
+ 'ES79 2100 0813 6101 2345 6789',
+ 'XX22YYY1234567890123',
+ 'FR14 2004 1010 0505 0001 3',
+ 'FR7630006000011234567890189@',
+ 'FR7630006000011234567890189😅',
+ 'FR763000600001123456!!🤨7890189@',
+ ],
+ });
+ test({
+ validator: 'isIBAN',
+ args: [{ blacklist: ['IT'] }],
+ valid: [
+ 'SC52BAHL01031234567890123456USD',
+ 'LC14BOSL123456789012345678901234',
+ 'MT31MALT01100000000000000000123',
+ 'SV43ACAT00000000000000123123',
+ 'EG800002000156789012345180002',
+ 'BE71 0961 2345 6769',
+ 'FR76 3000 6000 0112 3456 7890 189',
+ 'DE91 1000 0000 0123 4567 89',
+ 'GR96 0810 0010 0000 0123 4567 890',
+ 'RO09 BCYP 0000 0012 3456 7890',
+ 'SA44 2000 0001 2345 6789 1234',
+ 'ES79 2100 0813 6101 2345 6789',
+ 'CH56 0483 5012 3456 7800 9',
+ 'GB98 MIDL 0700 9312 3456 78',
+ 'IL170108000000012612345',
+ 'JO71CBJO0000000000001234567890',
+ 'TR320010009999901234567890',
+ 'BR1500000000000010932840814P2',
+ 'LB92000700000000123123456123',
+ 'IR200170000000339545727003',
+ 'MZ97123412341234123412341',
+ ],
+ invalid: [
+ 'XX22YYY1234567890123',
+ 'FR14 2004 1010 0505 0001 3',
+ 'FR7630006000011234567890189@',
+ 'FR7630006000011234567890189😅',
+ 'FR763000600001123456!!🤨7890189@',
+ 'IT60X0542811101000000123456',
+ ],
+ });
+ });
+
+ it('should validate BIC codes', () => {
+ test({
+ validator: 'isBIC',
+ valid: [
+ 'SBICKEN1345',
+ 'SBICKEN1',
+ 'SBICKENY',
+ 'SBICKEN1YYP',
+ 'SBICXKN1YYP',
+ ],
+ invalid: [
+ 'SBIC23NXXX',
+ 'S23CKENXXXX',
+ 'SBICKENXX',
+ 'SBICKENXX9',
+ 'SBICKEN13458',
+ 'SBICKEN',
+ 'SBICXK',
+ ],
+ });
+ });
+
+ it('should validate that integer strings are divisible by a number', () => {
+ test({
+ validator: 'isDivisibleBy',
+ args: [2],
+ valid: ['2', '4', '100', '1000'],
+ invalid: [
+ '1',
+ '2.5',
+ '101',
+ 'foo',
+ '',
+ '2020-01-06T14:31:00.135Z',
+ ],
+ });
+ });
+
+ it('should validate luhn numbers', () => {
+ test({
+ validator: 'isLuhnNumber',
+ valid: [
+ '0',
+ '5421',
+ '01234567897',
+ '0123456789012345678906',
+ '0123456789012345678901234567891',
+ '123456789012345678906',
+ '375556917985515',
+ '36050234196908',
+ '4716461583322103',
+ '4716-2210-5188-5662',
+ '4929 7226 5379 7141',
+ ],
+ invalid: [
+ '',
+ '1',
+ '5422',
+ 'foo',
+ 'prefix6234917882863855',
+ '623491788middle2863855',
+ '6234917882863855suffix',
+ ],
+ });
+ });
+
+ it('should validate credit cards', () => {
+ test({
+ validator: 'isCreditCard',
+ valid: [
+ '375556917985515',
+ '36050234196908',
+ '4716461583322103',
+ '4716-2210-5188-5662',
+ '4929 7226 5379 7141',
+ '5398228707871527',
+ '6283875070985593',
+ '6263892624162870',
+ '6234917882863855',
+ '6234698580215388',
+ '6226050967750613',
+ '6246281879460688',
+ '2222155765072228',
+ '2225855203075256',
+ '2720428011723762',
+ '2718760626256570',
+ '6765780016990268',
+ '4716989580001715211',
+ '8171999927660000',
+ '8171999900000000021',
+ ],
+ invalid: [
+ 'foo',
+ 'foo',
+ '5398228707871528',
+ '2718760626256571',
+ '2721465526338453',
+ '2220175103860763',
+ '375556917985515999999993',
+ '899999996234917882863855',
+ 'prefix6234917882863855',
+ '623491788middle2863855',
+ '6234917882863855suffix',
+ '4716989580001715213',
+ ],
+ });
+ });
+
+
+ it('should validate credit cards without a proper provider', () => {
+ test({
+ validator: 'isCreditCard',
+ args: [{ provider: 'Plorf' }],
+ error: [
+ 'foo',
+ // valid cc #
+ '375556917985515',
+ '4716-2210-5188-5662',
+ '375556917985515999999993',
+ '6234917882863855suffix',
+ ],
+ });
+ });
+
+
+ it('should validate AmEx provided credit cards', () => {
+ test({
+ validator: 'isCreditCard',
+ args: [{ provider: 'AmEx' }],
+ valid: [
+ '375556917985515',
+ ],
+ invalid: [
+ 'foo',
+ '2222155765072228',
+ '2225855203075256',
+ '2720428011723762',
+ '2718760626256570',
+ '36050234196908',
+ '375556917985515999999993',
+ '4716461583322103',
+ '4716-2210-5188-5662',
+ '4716989580001715211',
+ '4929 7226 5379 7141',
+ '5398228707871527',
+ '6234917882863855suffix',
+ '6283875070985593',
+ '6263892624162870',
+ '6234917882863855',
+ '6234698580215388',
+ '6226050967750613',
+ '6246281879460688',
+ '6283875070985593',
+ '6765780016990268',
+ '8171999927660000',
+ '8171999900000000021',
+ ],
+ });
+ });
+
+
+ it('should validate Diners Club provided credit cards', () => {
+ test({
+ validator: 'isCreditCard',
+ args: [{ provider: 'DinersClub' }],
+ valid: [
+ '36050234196908',
+ ],
+ invalid: [
+ 'foo',
+ '2222155765072228',
+ '2225855203075256',
+ '2720428011723762',
+ '2718760626256570',
+ '375556917985515',
+ '375556917985515999999993',
+ '4716461583322103',
+ '4716-2210-5188-5662',
+ '4716989580001715211',
+ '4929 7226 5379 7141',
+ '5398228707871527',
+ '6234917882863855suffix',
+ '6283875070985593',
+ '6263892624162870',
+ '6234917882863855',
+ '6234698580215388',
+ '6226050967750613',
+ '6246281879460688',
+ '6283875070985593',
+ '6765780016990268',
+ '8171999927660000',
+ '8171999900000000021',
+ ],
+ });
+ });
+
+ it('should validate Discover provided credit cards', () => {
+ test({
+ validator: 'isCreditCard',
+ args: [{ provider: 'Discover' }],
+ valid: [
+ '6011111111111117',
+ '6011000990139424',
+ ],
+ invalid: [
+ 'foo',
+ '2222155765072228',
+ '2225855203075256',
+ '2720428011723762',
+ '2718760626256570',
+ '36050234196908',
+ '375556917985515',
+ '375556917985515999999993',
+ '4716461583322103',
+ '4716-2210-5188-5662',
+ '4716989580001715211',
+ '4929 7226 5379 7141',
+ '5398228707871527',
+ '6234917882863855suffix',
+ '6283875070985593',
+ '6263892624162870',
+ '6234917882863855',
+ '6234698580215388',
+ '6226050967750613',
+ '6246281879460688',
+ '6283875070985593',
+ '6765780016990268',
+ '8171999927660000',
+ '8171999900000000021',
+ ],
+ });
+ });
+
+ it('should validate JCB provided credit cards', () => {
+ test({
+ validator: 'isCreditCard',
+ args: [{ provider: 'JCB' }],
+ valid: [
+ '3530111333300000',
+ '3566002020360505',
+ ],
+ invalid: [
+ 'foo',
+ '2222155765072228',
+ '2225855203075256',
+ '2720428011723762',
+ '2718760626256570',
+ '36050234196908',
+ '375556917985515',
+ '375556917985515999999993',
+ '4716461583322103',
+ '4716-2210-5188-5662',
+ '4716989580001715211',
+ '4929 7226 5379 7141',
+ '5398228707871527',
+ '6234917882863855suffix',
+ '6283875070985593',
+ '6263892624162870',
+ '6234917882863855',
+ '6234698580215388',
+ '6226050967750613',
+ '6246281879460688',
+ '6283875070985593',
+ '6765780016990268',
+ '8171999927660000',
+ '8171999900000000021',
+ ],
+ });
+ });
+
+
+ it('should validate Mastercard provided credit cards', () => {
+ test({
+ validator: 'isCreditCard',
+ args: [{ provider: 'Mastercard' }],
+ valid: [
+ '2222155765072228',
+ '2225855203075256',
+ '2718760626256570',
+ '2720428011723762',
+ '5398228707871527',
+ ],
+ invalid: [
+ 'foo',
+ '36050234196908',
+ '375556917985515',
+ '375556917985515999999993',
+ '4716461583322103',
+ '4716-2210-5188-5662',
+ '4716989580001715211',
+ '4929 7226 5379 7141',
+ '6234917882863855suffix',
+ '6283875070985593',
+ '6263892624162870',
+ '6234917882863855',
+ '6234698580215388',
+ '6226050967750613',
+ '6246281879460688',
+ '6283875070985593',
+ '6765780016990268',
+ '8171999927660000',
+ '8171999900000000021',
+ ],
+ });
+ });
+
+
+ it('should validate Union Pay provided credit cards', () => {
+ test({
+ validator: 'isCreditCard',
+ args: [{ provider: 'UnionPay' }],
+ valid: [
+ '6226050967750613',
+ '6234917882863855',
+ '6234698580215388',
+ '6246281879460688',
+ '6263892624162870',
+ '6283875070985593',
+ '6765780016990268',
+ '8171999927660000',
+ '8171999900000000021',
+ ],
+ invalid: [
+ 'foo',
+ '2222155765072228',
+ '2225855203075256',
+ '2720428011723762',
+ '2718760626256570',
+ '36050234196908',
+ '375556917985515',
+ '375556917985515999999993',
+ '4716461583322103',
+ '4716-2210-5188-5662',
+ '4716989580001715211',
+ '4929 7226 5379 7141',
+ '5398228707871527',
+ '6234917882863855suffix',
+ ],
+ });
+ });
+
+
+ it('should validate Visa provided credit cards', () => {
+ test({
+ validator: 'isCreditCard',
+ args: [{ provider: 'Visa' }],
+ valid: [
+ '4716-2210-5188-5662',
+ '4716461583322103',
+ '4716989580001715211',
+ '4929 7226 5379 7141',
+ ],
+ invalid: [
+ 'foo',
+ '2222155765072228',
+ '2225855203075256',
+ '2720428011723762',
+ '2718760626256570',
+ '36050234196908',
+ '375556917985515',
+ '375556917985515999999993',
+ '5398228707871527',
+ '6234917882863855suffix',
+ '6283875070985593',
+ '6263892624162870',
+ '6234917882863855',
+ '6234698580215388',
+ '6226050967750613',
+ '6246281879460688',
+ '6283875070985593',
+ '6765780016990268',
+ '8171999927660000',
+ '8171999900000000021',
+ ],
+ });
+ });
+
+
+ it('should validate identity cards', () => {
+ const fixtures = [
+ {
+ locale: 'PK',
+ valid: [
+ '45504-4185771-3',
+ '39915-6182971-9',
+ '21143-6182971-2',
+ '34543-2323471-1',
+ '72345-2345678-7',
+ '63456-8765432-8',
+ '55672-1234567-5',
+ '21234-9876543-6',
+ ],
+ invalid: [
+ '08000-1234567-5',
+ '74321-87654321-1',
+ '51234-98765-2',
+ '00000-0000000-0',
+ '88888-88888888-0',
+ '99999-9999999-9',
+ '11111',
+ ],
+ },
+ {
+ locale: 'zh-HK',
+ valid: [
+ 'OV290326[A]',
+ 'Q803337[0]',
+ 'Z0977986',
+ 'W520128(7)',
+ 'A494866[4]',
+ 'A494866(4)',
+ 'Z867821A',
+ 'ag293013(9)',
+ 'k348609(5)',
+ ],
+ invalid: [
+ 'A1234567890',
+ '98765432',
+ 'O962472(9)',
+ 'M4578601',
+ 'X731324[8]',
+ 'C503134(5)',
+ 'RH265886(3)',
+ ],
+ },
+ {
+ locale: 'LK',
+ valid: [
+ '722222222v',
+ '722222222V',
+ '993151225x',
+ '993151225X',
+ '188888388x',
+ '935632124V',
+ '199931512253',
+ '200023125632',
+ ],
+ invalid: [
+ '023125648V',
+ '023345621v',
+ '021354211X',
+ '055321231x',
+ '02135465462',
+ '199931512253X',
+ ],
+ },
+ {
+ locale: 'PL',
+ valid: [
+ '99012229019',
+ '09210215408',
+ '20313034701',
+ '86051575214',
+ '77334586883',
+ '54007481320',
+ '06566860643',
+ '77552478861',
+ ],
+ invalid: [
+ 'aa',
+ '5',
+ '195',
+ '',
+ ' ',
+ '12345678901',
+ '99212229019',
+ '09210215402',
+ '20313534701',
+ '86241579214',
+ ],
+ },
+ {
+ locale: 'ES',
+ valid: [
+ '99999999R',
+ '12345678Z',
+ '01234567L',
+ '01234567l',
+ 'X1234567l',
+ 'x1234567l',
+ 'X1234567L',
+ 'Y1234567X',
+ 'Z1234567R',
+ ],
+ invalid: [
+ '123456789',
+ '12345678A',
+ '12345 678Z',
+ '12345678-Z',
+ '1234*6789',
+ '1234*678Z',
+ '12345678!',
+ '1234567L',
+ 'A1234567L',
+ 'X1234567A',
+ 'Y1234567B',
+ 'Z1234567C',
+ ],
+ },
+ {
+ locale: 'FI',
+ valid: [
+ '131052-308T', // People born in 1900s
+ '131052A308T', // People born in 2000s
+ '131052+308T', // People born in 1800s
+ '131052-313Y',
+ ],
+ invalid: [
+ '131052308T',
+ '131052-308T ',
+ '131052-308A',
+ ],
+ },
+ {
+ locale: 'IN',
+ valid: [
+ '298448863364',
+ '2984 4886 3364',
+ ],
+ invalid: [
+ '99999999R',
+ '12345678Z',
+ '01234567L',
+ '01234567l',
+ 'X1234567l',
+ 'x1234567l',
+ 'X1234567L',
+ ],
+ },
+ {
+ locale: 'IR',
+ valid: [
+ '0499370899',
+ '0790419904',
+ '0084575948',
+ '0963695398',
+ '0684159414',
+ '0067749828',
+ '0650451252',
+ '1583250689',
+ '4032152314',
+ '0076229645',
+ '4271467685',
+ '0200203241',
+ ],
+ invalid: [
+ '1260293040',
+ '0000000001',
+ '1999999999',
+ '9999999991',
+ 'AAAAAAAAAA',
+ '0684159415',
+ ],
+ },
+ {
+ locale: 'IT',
+ valid: [
+ 'CR43675TM',
+ 'CA79382RA',
+ ],
+ invalid: [
+ 'CA00000AA',
+ 'CB2342TG',
+ 'CS123456A',
+ 'C1236EC',
+ ],
+ },
+ {
+ locale: 'NO',
+ valid: [
+ '09053426694',
+ '26028338723',
+ '08031470790',
+ '12051539514',
+ '02077448074',
+ '14035638319',
+ '13031379673',
+ '29126214926',
+ ],
+ invalid: [
+ '09053426699',
+ '00000000000',
+ '26028338724',
+ '92031470790',
+ ],
+ },
+ {
+ locale: 'TH',
+ valid: [
+ '1101230000001',
+ '1101230000060',
+ ],
+ invalid: [
+ 'abc',
+ '1101230',
+ '11012300000011',
+ 'aaaaaaaaaaaaa',
+ '110123abcd001',
+ '1101230000007',
+ '0101123450000',
+ '0101123450004',
+ '9101123450008',
+ ],
+ },
+ {
+ locale: 'he-IL',
+ valid: [
+ '219472156',
+ '219486610',
+ '219488962',
+ '219566726',
+ '219640216',
+ '219645041',
+ '334795465',
+ '335211686',
+ '335240479',
+ '335472171',
+ '336999842',
+ '337090443',
+ ],
+ invalid: [
+ '123456789',
+ '12345678A',
+ '12345 678Z',
+ '12345678-Z',
+ '1234*6789',
+ '1234*678Z',
+ '12345678!',
+ '1234567L',
+ 'A1234567L',
+ 'X1234567A',
+ 'Y1234567B',
+ 'Z1234567C',
+ '219772156',
+ '219487710',
+ '334705465',
+ '336000842',
+ ],
+ },
+ {
+ locale: 'ar-LY',
+ valid: [
+ '119803455876',
+ '120024679875',
+ '219624876201',
+ '220103480657',
+ ],
+ invalid: [
+ '987654320123',
+ '123-456-7890',
+ '012345678912',
+ '1234567890',
+ 'AFJBHUYTREWR',
+ 'C4V6B1X0M5T6',
+ '9876543210123',
+ ],
+ },
+ {
+ locale: 'ar-TN',
+ valid: [
+ '09958092',
+ '09151092',
+ '65126506',
+ '79378815',
+ '58994407',
+ '73089789',
+ '73260311',
+ ],
+ invalid: [
+ '123456789546',
+ '123456789',
+ '023456789',
+ '12345678A',
+ '12345',
+ '1234578A',
+ '123 578A',
+ '12345 678Z',
+ '12345678-Z',
+ '1234*6789',
+ '1234*678Z',
+ 'GE9800as98',
+ 'X231071922',
+ '1234*678Z',
+ '12345678!',
+ ],
+ },
+ {
+ locale: 'zh-CN',
+ valid: [
+ '235407195106112745',
+ '210203197503102721',
+ '520323197806058856',
+ '110101491001001',
+ ],
+ invalid: [
+ '160323197806058856',
+ '010203197503102721',
+ '520323297806058856',
+ '520323197802318856',
+ '235407195106112742',
+ '010101491001001',
+ '110101491041001',
+ '160101491001001',
+ '110101940231001',
+ 'xx1234567',
+ '135407195106112742',
+ '123456789546',
+ '123456789',
+ '023456789',
+ '12345678A',
+ '12345',
+ '1234578A',
+ '123 578A',
+ '12345 678Z',
+ '12345678-Z',
+ '1234*6789',
+ '1234*678Z',
+ 'GE9800as98',
+ 'X231071922',
+ '1234*678Z',
+ '12345678!',
+ '235407207006112742',
+ ],
+ },
+ {
+ locale: 'zh-TW',
+ valid: [
+ 'B176944193',
+ 'K101189797',
+ 'F112866121',
+ 'A219758834',
+ 'A244144802',
+ 'A146047171',
+ 'Q170219004',
+ 'Z277018381',
+ 'X231071923',
+ ],
+ invalid: [
+ '123456789',
+ 'A185034995',
+ 'X431071923',
+ 'GE9800as98',
+ 'X231071922',
+ '1234*678Z',
+ '12345678!',
+ '1234567L',
+ 'A1234567L',
+ 'X1234567A',
+ 'Y1234567B',
+ 'Z1234567C',
+ '219772156',
+ '219487710',
+ '334705465',
+ '336000842',
+ ],
+ },
+ ];
+
+ let allValid = [];
+
+ // Test fixtures
+ fixtures.forEach((fixture) => {
+ if (fixture.valid) allValid = allValid.concat(fixture.valid);
+ test({
+ validator: 'isIdentityCard',
+ valid: fixture.valid,
+ invalid: fixture.invalid,
+ args: [fixture.locale],
+ });
+ });
+
+ // Test generics
+ test({
+ validator: 'isIdentityCard',
+ valid: [
+ ...allValid,
+ ],
+ invalid: [
+ 'foo',
+ ],
+ args: ['any'],
+ });
+ });
+
+ it('should error on invalid locale', () => {
+ test({
+ validator: 'isIdentityCard',
+ args: ['is-NOT'],
+ error: [
+ '99999999R',
+ '12345678Z',
+ ],
+ });
+ });
+
+ it('should validate ISINs', () => {
+ test({
+ validator: 'isISIN',
+ valid: [
+ 'AU0000XVGZA3',
+ 'DE000BAY0017',
+ 'BE0003796134',
+ 'SG1G55870362',
+ 'GB0001411924',
+ 'DE000WCH8881',
+ 'PLLWBGD00016',
+ 'US0378331005',
+ ],
+ invalid: [
+ 'DE000BAY0018',
+ 'PLLWBGD00019',
+ 'foo',
+ '5398228707871528',
+ ],
+ });
+ });
+
+ it('should validate EANs', () => {
+ test({
+ validator: 'isEAN',
+ valid: [
+ '9421023610112',
+ '1234567890128',
+ '4012345678901',
+ '9771234567003',
+ '9783161484100',
+ '73513537',
+ '00012345600012',
+ '10012345678902',
+ '20012345678909',
+ ],
+ invalid: [
+ '5901234123451',
+ '079777681629',
+ '0705632085948',
+ ],
+ });
+ });
+
+ it('should validate ISSNs', () => {
+ test({
+ validator: 'isISSN',
+ valid: [
+ '0378-5955',
+ '0000-0000',
+ '2434-561X',
+ '2434-561x',
+ '01896016',
+ '20905076',
+ ],
+ invalid: [
+ '0378-5954',
+ '0000-0001',
+ '0378-123',
+ '037-1234',
+ '0',
+ '2434-561c',
+ '1684-5370',
+ '19960791',
+ '',
+ ],
+ });
+ test({
+ validator: 'isISSN',
+ args: [{ case_sensitive: true }],
+ valid: [
+ '2434-561X',
+ '2434561X',
+ '0378-5955',
+ '03785955',
+ ],
+ invalid: [
+ '2434-561x',
+ '2434561x',
+ ],
+ });
+ test({
+ validator: 'isISSN',
+ args: [{ require_hyphen: true }],
+ valid: [
+ '2434-561X',
+ '2434-561x',
+ '0378-5955',
+ ],
+ invalid: [
+ '2434561X',
+ '2434561x',
+ '03785955',
+ ],
+ });
+ test({
+ validator: 'isISSN',
+ args: [{ case_sensitive: true, require_hyphen: true }],
+ valid: [
+ '2434-561X',
+ '0378-5955',
+ ],
+ invalid: [
+ '2434-561x',
+ '2434561X',
+ '2434561x',
+ '03785955',
+ ],
+ });
+ });
+
+ it('should validate JSON', () => {
+ test({
+ validator: 'isJSON',
+ valid: [
+ '{ "key": "value" }',
+ '{}',
+ ],
+ invalid: [
+ '{ key: "value" }',
+ '{ \'key\': \'value\' }',
+ 'null',
+ '1234',
+ '"nope"',
+ ],
+ });
+ });
+
+ it('should validate JSON with primitives', () => {
+ test({
+ validator: 'isJSON',
+ args: [{ allow_primitives: true }],
+ valid: [
+ '{ "key": "value" }',
+ '{}',
+ 'null',
+ 'false',
+ 'true',
+ ],
+ invalid: [
+ '{ key: "value" }',
+ '{ \'key\': \'value\' }',
+ '{ "key": value }',
+ '1234',
+ '"nope"',
+ ],
+ });
+ });
+
+ it('should validate multibyte strings', () => {
+ test({
+ validator: 'isMultibyte',
+ valid: [
+ 'ひらがな・カタカナ、.漢字',
+ 'あいうえお foobar',
+ 'test@example.com',
+ '1234abcDExyz',
+ 'カタカナ',
+ '中文',
+ ],
+ invalid: [
+ 'abc',
+ 'abc123',
+ '<>@" *.',
+ ],
+ });
+ });
+
+ it('should validate ascii strings', () => {
+ test({
+ validator: 'isAscii',
+ valid: [
+ 'foobar',
+ '0987654321',
+ 'test@example.com',
+ '1234abcDEF',
+ ],
+ invalid: [
+ 'foobar',
+ 'xyz098',
+ '123456',
+ 'カタカナ',
+ ],
+ });
+ });
+
+ it('should validate full-width strings', () => {
+ test({
+ validator: 'isFullWidth',
+ valid: [
+ 'ひらがな・カタカナ、.漢字',
+ '3ー0 a@com',
+ 'Fカタカナ゙ᆲ',
+ 'Good=Parts',
+ ],
+ invalid: [
+ 'abc',
+ 'abc123',
+ '!"#$%&()<>/+=-_? ~^|.,@`{}[]',
+ ],
+ });
+ });
+
+ it('should validate half-width strings', () => {
+ test({
+ validator: 'isHalfWidth',
+ valid: [
+ '!"#$%&()<>/+=-_? ~^|.,@`{}[]',
+ 'l-btn_02--active',
+ 'abc123い',
+ 'カタカナ゙ᆲ←',
+ ],
+ invalid: [
+ 'あいうえお',
+ '0011',
+ ],
+ });
+ });
+
+ it('should validate variable-width strings', () => {
+ test({
+ validator: 'isVariableWidth',
+ valid: [
+ 'ひらがなカタカナ漢字ABCDE',
+ '3ー0123',
+ 'Fカタカナ゙ᆲ',
+ 'Good=Parts',
+ ],
+ invalid: [
+ 'abc',
+ 'abc123',
+ '!"#$%&()<>/+=-_? ~^|.,@`{}[]',
+ 'ひらがな・カタカナ、.漢字',
+ '123456',
+ 'カタカナ゙ᆲ',
+ ],
+ });
+ });
+
+ it('should validate surrogate pair strings', () => {
+ test({
+ validator: 'isSurrogatePair',
+ valid: [
+ '𠮷野𠮷',
+ '𩸽',
+ 'ABC千𥧄1-2-3',
+ ],
+ invalid: [
+ '吉野竈',
+ '鮪',
+ 'ABC1-2-3',
+ ],
+ });
+ });
+
+ it('should validate Semantic Versioning Specification (SemVer) strings', () => {
+ test({
+ validator: 'isSemVer',
+ valid: [
+ '0.0.4',
+ '1.2.3',
+ '10.20.30',
+ '1.1.2-prerelease+meta',
+ '1.1.2+meta',
+ '1.1.2+meta-valid',
+ '1.0.0-alpha',
+ '1.0.0-beta',
+ '1.0.0-alpha.beta',
+ '1.0.0-alpha.beta.1',
+ '1.0.0-alpha.1',
+ '1.0.0-alpha0.valid',
+ '1.0.0-alpha.0valid',
+ '1.0.0-alpha-a.b-c-somethinglong+build.1-aef.1-its-okay',
+ '1.0.0-rc.1+build.1',
+ '2.0.0-rc.1+build.123',
+ '1.2.3-beta',
+ '10.2.3-DEV-SNAPSHOT',
+ '1.2.3-SNAPSHOT-123',
+ '1.0.0',
+ '2.0.0',
+ '1.1.7',
+ '2.0.0+build.1848',
+ '2.0.1-alpha.1227',
+ '1.0.0-alpha+beta',
+ '1.2.3----RC-SNAPSHOT.12.9.1--.12+788',
+ '1.2.3----R-S.12.9.1--.12+meta',
+ '1.2.3----RC-SNAPSHOT.12.9.1--.12',
+ '1.0.0+0.build.1-rc.10000aaa-kk-0.1',
+ '99999999999999999999999.999999999999999999.99999999999999999',
+ '1.0.0-0A.is.legal',
+ ],
+ invalid: [
+ '-invalid+invalid',
+ '-invalid.01',
+ 'alpha',
+ 'alpha.beta',
+ 'alpha.beta.1',
+ 'alpha.1',
+ 'alpha+beta',
+ 'alpha_beta',
+ 'alpha.',
+ 'alpha..',
+ 'beta',
+ '1.0.0-alpha_beta',
+ '-alpha.',
+ '1.0.0-alpha..',
+ '1.0.0-alpha..1',
+ '1.0.0-alpha...1',
+ '1.0.0-alpha....1',
+ '1.0.0-alpha.....1',
+ '1.0.0-alpha......1',
+ '1.0.0-alpha.......1',
+ '01.1.1',
+ '1.01.1',
+ '1.1.01',
+ '1.2',
+ '1.2.3.DEV',
+ '1.2-SNAPSHOT',
+ '1.2.31.2.3----RC-SNAPSHOT.12.09.1--..12+788',
+ '1.2-RC-SNAPSHOT',
+ '-1.0.3-gamma+b7718',
+ '+justmeta',
+ '9.8.7+meta+meta',
+ '9.8.7-whatever+meta+meta',
+ '99999999999999999999999.999999999999999999.99999999999999999-',
+ '---RC-SNAPSHOT.12.09.1--------------------------------..12',
+ ],
+ });
+ });
+
+ it('should validate base32 strings', () => {
+ test({
+ validator: 'isBase32',
+ valid: [
+ 'ZG======',
+ 'JBSQ====',
+ 'JBSWY===',
+ 'JBSWY3A=',
+ 'JBSWY3DP',
+ 'JBSWY3DPEA======',
+ 'K5SWYY3PNVSSA5DPEBXG6ZA=',
+ 'K5SWYY3PNVSSA5DPEBXG6===',
+ ],
+ invalid: [
+ '12345',
+ '',
+ 'JBSWY3DPtesting123',
+ 'ZG=====',
+ 'Z======',
+ 'Zm=8JBSWY3DP',
+ '=m9vYg==',
+ 'Zm9vYm/y====',
+ ],
+ });
+ });
+
+ it('should validate base32 strings with crockford alternative', () => {
+ test({
+ validator: 'isBase32',
+ args: [{ crockford: true }],
+ valid: [
+ '91JPRV3F41BPYWKCCGGG',
+ '60',
+ '64',
+ 'B5QQA833C5Q20S3F41MQ8',
+ ],
+ invalid: [
+ '91JPRV3F41BUPYWKCCGGG',
+ 'B5QQA833C5Q20S3F41MQ8L',
+ '60I',
+ 'B5QQA833OULIC5Q20S3F41MQ8',
+ ],
+ });
+ });
+
+ it('should validate base58 strings', () => {
+ test({
+ validator: 'isBase58',
+ valid: [
+ 'BukQL',
+ '3KMUV89zab',
+ '91GHkLMNtyo98',
+ 'YyjKm3H',
+ 'Mkhss145TRFg',
+ '7678765677',
+ 'abcodpq',
+ 'AAVHJKLPY',
+ ],
+ invalid: [
+ '0OPLJH',
+ 'IMKLP23',
+ 'KLMOmk986',
+ 'LL1l1985hG',
+ '*MP9K',
+ 'Zm=8JBSWY3DP',
+ ')()(=9292929MKL',
+ ],
+ });
+ });
+
+
+ it('should validate hex-encoded MongoDB ObjectId', () => {
+ test({
+ validator: 'isMongoId',
+ valid: [
+ '507f1f77bcf86cd799439011',
+ ],
+ invalid: [
+ '507f1f77bcf86cd7994390',
+ '507f1f77bcf86cd79943901z',
+ '',
+ '507f1f77bcf86cd799439011 ',
+ ],
+ });
+ });
+
+ it('should define the module using an AMD-compatible loader', () => {
+ let window = {
+ validator: null,
+ define(module) {
+ window.validator = module();
+ },
+ };
+ window.define.amd = true;
+
+ let sandbox = vm.createContext(window);
+ vm.runInContext(validator_js, sandbox);
+ assert.strictEqual(window.validator.trim(' foobar '), 'foobar');
+ });
+
+ it('should bind validator to the window if no module loaders are available', () => {
+ let window = {};
+ let sandbox = vm.createContext(window);
+ vm.runInContext(validator_js, sandbox);
+ assert.strictEqual(window.validator.trim(' foobar '), 'foobar');
+ });
+
+ it('should validate mobile phone number', () => {
+ let fixtures = [
+ {
+ locale: 'am-AM',
+ valid: [
+ '+37433123456',
+ '+37441123456',
+ '+37443123456',
+ '+37444123456',
+ '+37455123456',
+ '+37477123456',
+ '+37488123456',
+ '+37491123456',
+ '+37493123456',
+ '+37494123456',
+ '+37495123456',
+ '+37496123456',
+ '+37498123456',
+ '+37499123456',
+ '055123456',
+ '37455123456',
+ ],
+ invalid: [
+ '12345',
+ '+37403498855',
+ '+37416498123',
+ '05614988556',
+ '',
+ '37456789000',
+ '37486789000',
+ '+37431312345',
+ '+37430312345',
+ '+37460123456',
+ '+37410324123',
+ '+37422298765',
+ '+37431276521',
+ '022698763',
+ '+37492123456',
+ ],
+ },
+ {
+ locale: 'ar-AE',
+ valid: [
+ '+971502674453',
+ '+971521247658',
+ '+971541255684',
+ '+971555454458',
+ '+971561498855',
+ '+971585215778',
+ '971585215778',
+ '0585215778',
+ '585215778',
+ ],
+ invalid: [
+ '12345',
+ '+971511498855',
+ '+9715614988556',
+ '+9745614988556',
+ '',
+ '+9639626626262',
+ '+963332210972',
+ '0114152198',
+ '962796477263',
+ ],
+ },
+ {
+ locale: 'ar-BH',
+ valid: [
+ '+97335078110',
+ '+97339534385',
+ '+97366331055',
+ '+97333146000',
+ '97335078110',
+ '35078110',
+ '66331055',
+ ],
+ invalid: [
+ '12345',
+ '+973350781101',
+ '+97379534385',
+ '+973035078110',
+ '',
+ '+9639626626262',
+ '+963332210972',
+ '0114152198',
+ '962796477263',
+ '035078110',
+ '16331055',
+ 'hello',
+ '+9733507811a',
+ ],
+ },
+ {
+ locale: 'ar-EG',
+ valid: [
+ '+201004513789',
+ '+201111453489',
+ '+201221204610',
+ '+201144621154',
+ '+201200124304',
+ '+201011201564',
+ '+201124679001',
+ '+201064790156',
+ '+201274652177',
+ '+201280134679',
+ '+201090124576',
+ '+201583728900',
+ '201599495596',
+ '201090124576',
+ '01090124576',
+ '01538920744',
+ '1593075993',
+ '1090124576',
+ ],
+ invalid: [
+ '+221004513789',
+ '+201404513789',
+ '12345',
+ '',
+ '+9639626626262',
+ '+963332210972',
+ '0114152198',
+ '962796477263',
+ ],
+ },
+ {
+ locale: 'ar-JO',
+ valid: [
+ '0796477263',
+ '0777866254',
+ '0786725261',
+ '+962796477263',
+ '+962777866254',
+ '+962786725261',
+ '962796477263',
+ '962777866254',
+ '962786725261',
+ ],
+ invalid: [
+ '00962786725261',
+ '00962796477263',
+ '12345',
+ '',
+ '+9639626626262',
+ '+963332210972',
+ '0114152198',
+ ],
+ },
+ {
+ locale: 'ar-KW',
+ valid: [
+ '96550000000',
+ '96560000000',
+ '96590000000',
+ '96541000000',
+ '+96550000000',
+ '+96550000220',
+ '+96551111220',
+ '+96541000000',
+ ],
+ invalid: [
+ '+96570000220',
+ '00962786725261',
+ '00962796477263',
+ '12345',
+ '',
+ '+9639626626262',
+ '+963332210972',
+ '0114152198',
+ '+96540000000',
+ ],
+ },
+ {
+ locale: 'ar-LB',
+ valid: [
+ '+96171234568',
+ '+9613123456',
+ '3456123',
+ '3123456',
+ '81978468',
+ '77675798',
+ ],
+ invalid: [
+ '+961712345688888',
+ '00912220000',
+ '7767579888',
+ '+0921110000',
+ '+3123456888',
+ '021222200000',
+ '213333444444',
+ '',
+ '+212234',
+ '+21',
+ '02122333',
+ ],
+ },
+ {
+ locale: 'ar-LY',
+ valid: [
+ '912220000',
+ '0923330000',
+ '218945550000',
+ '+218958880000',
+ '212220000',
+ '0212220000',
+ '+218212220000',
+ ],
+ invalid: [
+ '9122220000',
+ '00912220000',
+ '09211110000',
+ '+0921110000',
+ '+2180921110000',
+ '021222200000',
+ '213333444444',
+ '',
+ '+212234',
+ '+21',
+ '02122333',
+ ],
+ },
+ {
+ locale: 'ar-MA',
+ valid: [
+ '0522714782',
+ '0690851123',
+ '0708186135',
+ '+212522714782',
+ '+212690851123',
+ '+212708186135',
+ '00212522714782',
+ '00212690851123',
+ '00212708186135',
+ ],
+ invalid: [
+ '522714782',
+ '690851123',
+ '708186135',
+ '212522714782',
+ '212690851123',
+ '212708186135',
+ '0212522714782',
+ '0212690851123',
+ '0212708186135',
+ '',
+ '12345',
+ '0922714782',
+ '+212190851123',
+ '00212408186135',
+ ],
+ },
+ {
+ locale: 'dz-BT',
+ valid: [
+ '+97517374354',
+ '+97517454971',
+ '77324646',
+ '016329712',
+ '97517265559',
+ ],
+ invalid: [
+ '',
+ '9898347255',
+ '+96326626262',
+ '963372',
+ '0114152198',
+ ],
+ },
+ {
+ locale: 'ar-OM',
+ valid: [
+ '+96891212121',
+ '+96871212121',
+ '0096899999999',
+ '93112211',
+ '99099009',
+ ],
+ invalid: [
+ '+96890212121',
+ '0096890999999',
+ '0090999999',
+ '+9689021212',
+ '',
+ '+212234',
+ '+21',
+ '02122333',
+ ],
+ },
+ {
+ locale: 'ar-PS',
+ valid: [
+ '+970563459876',
+ '970592334218',
+ '0566372345',
+ '0598273583',
+ ],
+ invalid: [
+ '+9759029487',
+ '97059123456789',
+ '598372348',
+ '97058aaaafjd',
+ '',
+ '05609123484',
+ '+97059',
+ '+970',
+ '97056',
+ ],
+ },
+ {
+ locale: 'ar-SY',
+ valid: [
+ '0944549710',
+ '+963944549710',
+ '956654379',
+ '0944549710',
+ '0962655597',
+ ],
+ invalid: [
+ '12345',
+ '',
+ '+9639626626262',
+ '+963332210972',
+ '0114152198',
+ ],
+ },
+ {
+ locale: 'ar-SA',
+ valid: [
+ '0556578654',
+ '+966556578654',
+ '966556578654',
+ '596578654',
+ '572655597',
+ ],
+ invalid: [
+ '12345',
+ '',
+ '+9665626626262',
+ '+96633221097',
+ '0114152198',
+ ],
+ },
+ {
+ locale: 'ar-SD',
+ valid: [
+ '0128652312',
+ '+249919425113',
+ '249123212345',
+ '0993212345',
+ ],
+ invalid: [
+ '12345',
+ '',
+ '+249972662622',
+ '+24946266262',
+ '+24933221097',
+ '0614152198',
+ '096554',
+ ],
+ },
+ {
+ locale: 'ar-TN',
+ valid: [
+ '23456789',
+ '+21623456789',
+ '21623456789',
+ ],
+ invalid: [
+ '12345',
+ '75200123',
+ '+216512345678',
+ '13520459',
+ '85479520',
+ ],
+ },
+ {
+ locale: 'bg-BG',
+ valid: [
+ '+359897123456',
+ '+359898888888',
+ '0897123123',
+ ],
+ invalid: [
+ '',
+ '0898123',
+ '+359212555666',
+ '18001234567',
+ '12125559999',
+ ],
+ },
+ {
+ locale: 'bn-BD',
+ valid: [
+ '+8801794626846',
+ '01399098893',
+ '8801671163269',
+ '01717112029',
+ '8801898765432',
+ '+8801312345678',
+ '01494676946',
+ ],
+ invalid: [
+ '',
+ '0174626346',
+ '017943563469',
+ '18001234567',
+ '0131234567',
+ ],
+ },
+ {
+ locale: 'bs-BA',
+ valid: [
+ '060123456',
+ '061123456',
+ '062123456',
+ '063123456',
+ '0641234567',
+ '065123456',
+ '066123456',
+ '+38760123456',
+ '+38761123456',
+ '+38762123456',
+ '+38763123456',
+ '+387641234567',
+ '+38765123456',
+ '+38766123456',
+ '0038760123456',
+ '0038761123456',
+ '0038762123456',
+ '0038763123456',
+ '00387641234567',
+ '0038765123456',
+ '0038766123456',
+ ],
+ invalid: [
+ '0601234567',
+ '0611234567',
+ '06212345',
+ '06312345',
+ '064123456',
+ '0651234567',
+ '06612345',
+ '+3866123456',
+ '+3856123456',
+ '00038760123456',
+ '038761123456',
+ ],
+ },
+ {
+ locale: 'cs-CZ',
+ valid: [
+ '+420 123 456 789',
+ '+420 123456789',
+ '+420123456789',
+ '123 456 789',
+ '123456789',
+ ],
+ invalid: [
+ '',
+ '+42012345678',
+ '+421 123 456 789',
+ '+420 023456789',
+ '+4201234567892',
+ ],
+ },
+ {
+ locale: 'sk-SK',
+ valid: [
+ '+421 123 456 789',
+ '+421 123456789',
+ '+421123456789',
+ '123 456 789',
+ '123456789',
+ ],
+ invalid: [
+ '',
+ '+42112345678',
+ '+422 123 456 789',
+ '+421 023456789',
+ '+4211234567892',
+ ],
+ },
+ {
+ locale: 'de-DE',
+ valid: [
+ '+4915123456789',
+ '015123456789',
+ '015123456789',
+ '015623456789',
+ '015623456789',
+ '01601234567',
+ '016012345678',
+ '01621234567',
+ '01631234567',
+ '01701234567',
+ '017612345678',
+ ],
+ invalid: [
+ '+4930405044550',
+ '34412345678',
+ '14412345678',
+ '16212345678',
+ '1761234567',
+ '16412345678',
+ '17012345678',
+ '+4912345678910',
+ '+49015123456789',
+ '015345678910',
+ '015412345678',
+ ],
+ },
+ {
+ locale: 'de-AT',
+ valid: [
+ '+436761234567',
+ '06761234567',
+ '00436123456789',
+ '+436123456789',
+ '01999',
+ '+4372876',
+ '06434908989562345',
+ ],
+ invalid: [
+ '167612345678',
+ '1234',
+ '064349089895623459',
+ ],
+ },
+ {
+ locale: 'hu-HU',
+ valid: [
+ '06301234567',
+ '+36201234567',
+ '06701234567',
+ ],
+ invalid: [
+ '1234',
+ '06211234567',
+ '+3620123456',
+ ],
+ },
+ {
+ locale: 'mz-MZ',
+ valid: [
+ '+258849229754',
+ '258849229754',
+ '849229754',
+ '829229754',
+ '839229754',
+ '869229754',
+ '859229754',
+ '869229754',
+ '879229754',
+ '+258829229754',
+ '+258839229754',
+ '+258869229754',
+ '+258859229754',
+ '+258869229754',
+ '+258879229754',
+ '258829229754',
+ '258839229754',
+ '258869229754',
+ '258859229754',
+ '258869229754',
+ '258879229754',
+ ],
+ invalid: [
+ '+248849229754',
+ '158849229754',
+ '249229754',
+ '819229754',
+ '899229754',
+ '889229754',
+ '89229754',
+ '8619229754',
+ '87922975411',
+ '257829229754',
+ '+255839229754',
+ '+2258869229754',
+ '+1258859229754',
+ '+2588692297541',
+ '+2588792519754',
+ '25882922975411',
+ ],
+ },
+ {
+ locale: 'pt-BR',
+ valid: [
+ '+55 12 996551215',
+ '+55 15 97661234',
+ '+55 (12) 996551215',
+ '+55 (15) 97661234',
+ '55 (17) 96332-2155',
+ '55 (17) 6332-2155',
+ '55 15 976612345',
+ '55 15 75661234',
+ '+5512984567890',
+ '+551283456789',
+ '5512984567890',
+ '551283456789',
+ '015994569878',
+ '01593456987',
+ '022995678947',
+ '02299567894',
+ '(22)99567894',
+ '(22)9956-7894',
+ '(22) 99567894',
+ '(22) 9956-7894',
+ '(22)999567894',
+ '(22)99956-7894',
+ '(22) 999567894',
+ '(22) 99956-7894',
+ '(11) 94123-4567',
+ '(11) 91431-4567',
+ '+55 (11) 91431-4567',
+ '+55 11 91431-4567',
+ '+551191431-4567',
+ '5511914314567',
+ '5511912345678',
+ ],
+ invalid: [
+ '0819876543',
+ '+55 15 7566123',
+ '+017 123456789',
+ '5501599623874',
+ '+55012962308',
+ '+55 015 1234-3214',
+ '+55 11 90431-4567',
+ '+55 (11) 90431-4567',
+ '+551190431-4567',
+ '5511904314567',
+ '5511902345678',
+ '(11) 90431-4567',
+ ],
+ },
+ {
+ locale: 'zh-CN',
+ valid: [
+ '13523333233',
+ '13838389438',
+ '14899230918',
+ '14999230918',
+ '15323456787',
+ '15052052020',
+ '16237108167',
+ '008616238234822',
+ '+8616238234822',
+ '16565600001',
+ '17269427292',
+ '17469427292',
+ '18199617480',
+ '19151751717',
+ '19651751717',
+ '+8613238234822',
+ '+8613487234567',
+ '+8617823492338',
+ '+8617823492338',
+ '+8616637108167',
+ '+8616637108167',
+ '+8616712341234',
+ '+8619912341234',
+ '+8619812341234',
+ '+8619712341234',
+ '+8619612341234',
+ '+8619512341234',
+ '+8619312341234',
+ '+8619212341234',
+ '+8619112341234',
+ '+8617269427292',
+ '008618812341234',
+ '008618812341234',
+ '008617269427292',
+ // Reserve number segments in the future.
+ '92138389438',
+ '+8692138389438',
+ '008692138389438',
+ '98199649964',
+ '+8698099649964',
+ '008698099649964',
+ ],
+ invalid: [
+ '12345',
+ '',
+ '12038389438',
+ '12838389438',
+ '013838389438',
+ '+86-13838389438',
+ '+08613811211114',
+ '+008613811211114',
+ '08613811211114',
+ '0086-13811211114',
+ '0086-138-1121-1114',
+ 'Vml2YW11cyBmZXJtZtesting123',
+ '010-38238383',
+ ],
+ },
+ {
+ locale: 'zh-TW',
+ valid: [
+ '0987123456',
+ '+886987123456',
+ '886987123456',
+ '+886-987123456',
+ '886-987123456',
+ ],
+ invalid: [
+ '12345',
+ '',
+ 'Vml2YW11cyBmZXJtZtesting123',
+ '0-987123456',
+ ],
+ },
+ {
+ locale: 'en-LS',
+ valid: [
+ '+26622123456',
+ '+26628123456',
+ '+26657123456',
+ '+26658123456',
+ '+26659123456',
+ '+26627123456',
+ '+26652123456',
+ ],
+ invalid: [
+ '+26612345678',
+ '',
+ '2664512-21',
+ '+2662212345678',
+ 'someString',
+ ],
+ },
+ {
+ locale: 'en-BM',
+ valid: [
+ '+14417974653',
+ '14413986653',
+ '4415370973',
+ '+14415005489',
+ ],
+ invalid: [
+ '85763287',
+ '+14412020436',
+ '+14412236546',
+ '+14418245567',
+ '+14416546789',
+ '44087635627',
+ '+4418970973',
+ '',
+ '+1441897465',
+ '+1441897465 additional invalid string part',
+ ],
+ },
+ {
+ locale: 'en-BS',
+ valid: [
+ '+12421231234',
+ '2421231234',
+ '+1-2421231234',
+ '+1-242-123-1234',
+ '(242)-123-1234',
+ '+1 (242)-123-1234',
+ '242 123-1234',
+ '(242) 123 1234',
+ ],
+ invalid: [
+ '85763287',
+ '+1 242 12 12 12 12',
+ '+1424123123',
+ '+14418245567',
+ '+14416546789',
+ 'not a number',
+ '',
+ ],
+ },
+ {
+ locale: 'en-ZA',
+ valid: [
+ '0821231234',
+ '+27821231234',
+ '27821231234',
+ ],
+ invalid: [
+ '082123',
+ '08212312345',
+ '21821231234',
+ '+21821231234',
+ '+0821231234',
+ ],
+ },
+ {
+ locale: 'en-AU',
+ valid: [
+ '61404111222',
+ '+61411222333',
+ '0417123456',
+ ],
+ invalid: [
+ '082123',
+ '08212312345',
+ '21821231234',
+ '+21821231234',
+ '+0821231234',
+ '04123456789',
+ ],
+ },
+ {
+ locale: 'es-BO',
+ valid: [
+ '+59175553635',
+ '+59162223685',
+ '+59179783890',
+ '+59160081890',
+ '79783890',
+ '60081890',
+ ],
+ invalid: [
+ '082123',
+ '08212312345',
+ '21821231234',
+ '+21821231234',
+ '+59199783890',
+ ],
+ },
+ {
+ locale: 'en-GG',
+ valid: [
+ '+441481123456',
+ '+441481789123',
+ '441481123456',
+ '441481789123',
+ ],
+ invalid: [
+ '999',
+ '+441481123456789',
+ '+447123456789',
+ ],
+ },
+ {
+ locale: 'en-GH',
+ valid: [
+ '0202345671',
+ '0502345671',
+ '0242345671',
+ '0542345671',
+ '0532345671',
+ '0272345671',
+ '0572345671',
+ '0262345671',
+ '0562345671',
+ '0232345671',
+ '0282345671',
+ '+233202345671',
+ '+233502345671',
+ '+233242345671',
+ '+233542345671',
+ '+233532345671',
+ '+233272345671',
+ '+233572345671',
+ '+233262345671',
+ '+233562345671',
+ '+233232345671',
+ '+233282345671',
+ '+233592349493',
+ '0550298219',
+ ],
+ invalid: [
+ '082123',
+ '232345671',
+ '0292345671',
+ '+233292345671',
+ ],
+ },
+ {
+ locale: 'en-GY',
+ valid: [
+ '+5926121234',
+ '06121234',
+ '06726381',
+ '+5926726381',
+ ],
+ invalid: [
+ '5926121234',
+ '6121234',
+ '+592 6121234',
+ '05926121234',
+ '+592-6121234',
+ ],
+ },
+ {
+ locale: 'en-HK',
+ valid: [
+ '91234567',
+ '9123-4567',
+ '61234567',
+ '51234567',
+ '+85291234567',
+ '+852-91234567',
+ '+852-9123-4567',
+ '+852 9123 4567',
+ '9123 4567',
+ '852-91234567',
+ ],
+ invalid: [
+ '999',
+ '+852-912345678',
+ '123456789',
+ '+852-1234-56789',
+ ],
+ },
+ {
+ locale: 'en-MO',
+ valid: [
+ '61234567',
+ '+85361234567',
+ '+853-61234567',
+ '+853-6123-4567',
+ '+853 6123 4567',
+ '6123 4567',
+ '853-61234567',
+ ],
+ invalid: [
+ '999',
+ '12345678',
+ '612345678',
+ '+853-12345678',
+ '+853-22345678',
+ '+853-82345678',
+ '+853-612345678',
+ '+853-1234-5678',
+ '+853 1234 5678',
+ '+853-6123-45678',
+ ],
+ },
+ {
+ locale: 'en-IE',
+ valid: [
+ '+353871234567',
+ '353831234567',
+ '353851234567',
+ '353861234567',
+ '353871234567',
+ '353881234567',
+ '353891234567',
+ '0871234567',
+ '0851234567',
+ ],
+ invalid: [
+ '999',
+ '+353341234567',
+ '+33589484858',
+ '353841234567',
+ '353811234567',
+ ],
+ },
+ {
+ locale: 'en-JM',
+ valid: [
+ '+8761021234',
+ '8761211234',
+ '8763511274',
+ '+8764511274',
+ ],
+ invalid: [
+ '999',
+ '+876102123422',
+ '+8861021234',
+ '8761021212213',
+ '876102123',
+ ],
+ },
+ {
+ locale: 'en-KE',
+ valid: [
+ '+254728590432',
+ '+254733875610',
+ '254728590234',
+ '0733346543',
+ '0700459022',
+ '0110934567',
+ '+254110456794',
+ '254198452389',
+ ],
+ invalid: [
+ '999',
+ '+25489032',
+ '123456789',
+ '+254800723845',
+ ],
+ },
+ {
+ locale: 'fr-CF',
+ valid: [
+ '+23670850000',
+ '+23675038756',
+ '+23677859002',
+ '+23672854202',
+ '+23621854052',
+ '+23622854072',
+ '72234650',
+ '70045902',
+ '77934567',
+ '21456794',
+ '22452389',
+ ],
+ invalid: [
+ '+23689032',
+ '123456789',
+ '+236723845987',
+ '022452389',
+ '+236772345678',
+ '+236700456794',
+
+ ],
+ },
+ {
+ locale: 'en-KI',
+ valid: [
+ '+68673140000',
+ '68673059999',
+ '+68663000000',
+ '68663019999',
+ ],
+ invalid: [
+ '+68653000000',
+ '68664019999',
+ '+68619019999',
+ '686123456789',
+ '+686733445',
+ ],
+ },
+ {
+ locale: 'en-MT',
+ valid: [
+ '+35699000000',
+ '+35679000000',
+ '99000000',
+ ],
+ invalid: [
+ '356',
+ '+35699000',
+ '+35610000000',
+ ],
+ },
+ {
+ locale: 'en-PH',
+ valid: [
+ '+639275149120',
+ '+639275142327',
+ '+639003002023',
+ '09275149116',
+ '09194877624',
+ ],
+ invalid: [
+ '12112-13-345',
+ '12345678901',
+ 'sx23YW11cyBmZxxXJt123123',
+ '010-38238383',
+ '966684123123-2590',
+ ],
+ },
+ {
+ locale: 'en-UG',
+ valid: [
+ '+256728590432',
+ '+256733875610',
+ '256728590234',
+ '0773346543',
+ '0700459022',
+ ],
+ invalid: [
+ '999',
+ '+254728590432',
+ '+25489032',
+ '123456789',
+ '+254800723845',
+ ],
+ },
+ {
+ locale: 'en-RW',
+ valid: [
+ '+250728590432',
+ '+250733875610',
+ '250738590234',
+ '0753346543',
+ '0780459022',
+ ],
+ invalid: [
+ '999',
+ '+254728590432',
+ '+25089032',
+ '123456789',
+ '+250800723845',
+ ],
+ },
+ {
+ locale: 'en-TZ',
+ valid: [
+ '+255728590432',
+ '+255733875610',
+ '255628590234',
+ '0673346543',
+ '0600459022',
+ ],
+ invalid: [
+ '999',
+ '+254728590432',
+ '+25589032',
+ '123456789',
+ '+255800723845',
+ ],
+ },
+ {
+ locale: 'en-MW',
+ valid: [
+ '+265994563785',
+ '+265111785436',
+ '+265318596857',
+ '0320008744',
+ '01256258',
+ '0882541896',
+ '+265984563214',
+ ],
+ invalid: [
+ '58563',
+ '+2658256258',
+ '0896328741',
+ '0708574896',
+ '+26570857489635',
+ ],
+ },
+ {
+ locale: 'es-PE',
+ valid: [
+ '+51912232764',
+ '+51923464567',
+ '+51968267382',
+ '+51908792973',
+ '974980472',
+ '908792973',
+ '+51974980472',
+ ],
+ invalid: [
+ '999',
+ '+51812232764',
+ '+5181223276499',
+ '+25589032',
+ '123456789',
+ ],
+ },
+ {
+ locale: 'fr-FR',
+ valid: [
+ '0612457898',
+ '+33612457898',
+ '33612457898',
+ '0712457898',
+ '+33712457898',
+ '33712457898',
+ ],
+ invalid: [
+ '061245789',
+ '06124578980',
+ '0112457898',
+ '0212457898',
+ '0312457898',
+ '0412457898',
+ '0512457898',
+ '0812457898',
+ '0912457898',
+ '+34612457898',
+ '+336124578980',
+ '+3361245789',
+ ],
+ },
+ {
+ locale: 'fr-BF',
+ valid: [
+ '+22661245789',
+ '+22665903092',
+ '+22672457898',
+ '+22673572346',
+ '061245789',
+ '071245783',
+ ],
+ invalid: [
+ '0612457892',
+ '06124578980',
+ '0112457898',
+ '0212457898',
+ '0312457898',
+ '0412457898',
+ '0512457898',
+ '0812457898',
+ '0912457898',
+ '+22762457898',
+ '+226724578980',
+ '+22634523',
+ ],
+ },
+ {
+ locale: 'fr-BJ',
+ valid: [
+ '+22920215789',
+ '+22920293092',
+ '+22921307898',
+ '+22921736346',
+ '+22922416346',
+ '+22923836346',
+ ],
+ invalid: [
+ '0612457892',
+ '01122921737346',
+ '+22762457898',
+ '+226724578980',
+ '+22634523',
+ ],
+ },
+ {
+ locale: 'fr-CA',
+ valid: ['19876543210', '8005552222', '+15673628910'],
+ invalid: [
+ '564785',
+ '0123456789',
+ '1437439210',
+ '+10345672645',
+ '11435213543',
+ ],
+ },
+ {
+ locale: 'fr-CD',
+ valid: [
+ '+243818590432',
+ '+243893875610',
+ '243978590234',
+ '0813346543',
+ '0820459022',
+ '+243902590221',
+ ],
+ invalid: [
+ '243',
+ '+254818590432',
+ '+24389032',
+ '123456789',
+ '+243700723845',
+ ],
+ },
+ {
+ locale: 'fr-GF',
+ valid: [
+ '0612457898',
+ '+594612457898',
+ '594612457898',
+ '0712457898',
+ '+594712457898',
+ '594712457898',
+ ],
+ invalid: [
+ '061245789',
+ '06124578980',
+ '0112457898',
+ '0212457898',
+ '0312457898',
+ '0412457898',
+ '0512457898',
+ '0812457898',
+ '0912457898',
+ '+54612457898',
+ '+5946124578980',
+ '+59461245789',
+ ],
+ },
+ {
+ locale: 'fr-GP',
+ valid: [
+ '0612457898',
+ '+590612457898',
+ '590612457898',
+ '0712457898',
+ '+590712457898',
+ '590712457898',
+ ],
+ invalid: [
+ '061245789',
+ '06124578980',
+ '0112457898',
+ '0212457898',
+ '0312457898',
+ '0412457898',
+ '0512457898',
+ '0812457898',
+ '0912457898',
+ '+594612457898',
+ '+5906124578980',
+ '+59061245789',
+ ],
+ },
+ {
+ locale: 'fr-MQ',
+ valid: [
+ '0612457898',
+ '+596612457898',
+ '596612457898',
+ '0712457898',
+ '+596712457898',
+ '596712457898',
+ ],
+ invalid: [
+ '061245789',
+ '06124578980',
+ '0112457898',
+ '0212457898',
+ '0312457898',
+ '0412457898',
+ '0512457898',
+ '0812457898',
+ '0912457898',
+ '+594612457898',
+ '+5966124578980',
+ '+59661245789',
+ ],
+ },
+ {
+ locale: 'fr-RE',
+ valid: [
+ '0612457898',
+ '+262612457898',
+ '262612457898',
+ '0712457898',
+ '+262712457898',
+ '262712457898',
+ ],
+ invalid: [
+ '061245789',
+ '06124578980',
+ '0112457898',
+ '0212457898',
+ '0312457898',
+ '0412457898',
+ '0512457898',
+ '0812457898',
+ '0912457898',
+ '+264612457898',
+ '+2626124578980',
+ '+26261245789',
+ ],
+ },
+ {
+ locale: 'fr-PF',
+ valid: [
+ '87123456',
+ '88123456',
+ '89123456',
+ '+68987123456',
+ '+68988123456',
+ '+68989123456',
+ '68987123456',
+ '68988123456',
+ '68989123456',
+ ],
+ invalid: [
+ '7123456',
+ '86123456',
+ '87 12 34 56',
+ 'definitely not a number',
+ '01+68988123456',
+ '6898912345',
+ ],
+ },
+ {
+ locale: 'fr-WF',
+ valid: [
+ '+681408500',
+ '+681499387',
+ '+681728590',
+ '+681808542',
+ '+681828540',
+ '+681832014',
+ '408500',
+ '499387',
+ '728590',
+ '808542',
+ '828540',
+ '832014',
+ ],
+ invalid: [
+ '+68189032',
+ '123456789',
+ '+681723845987',
+ '022452389',
+ '+681772345678',
+ '+681700456794',
+
+ ],
+ },
+ {
+ locale: 'ka-GE',
+ valid: [
+ '+995500011111',
+ '+995515352134',
+ '+995798526662',
+ '798526662',
+ '500011119',
+ '798526662',
+ '+995799766525',
+ ],
+ invalid: [
+ '+99550001111',
+ '+9957997665250',
+ '+9959997665251',
+ '+995780011111',
+ '20000000000',
+ '68129485729',
+ '6589394827',
+ '298RI89572',
+ ],
+ },
+ {
+ locale: 'el-GR',
+ valid: [
+ '+306944848966',
+ '306944848966',
+ '06904567890',
+ '6944848966',
+ '6904567890',
+ '6914567890',
+ '6934567890',
+ '6944567890',
+ '6954567890',
+ '6974567890',
+ '6984567890',
+ '6994567890',
+ '6854567890',
+ '6864567890',
+ '6874567890',
+ '6884567890',
+ '6894567890',
+ ],
+ invalid: [
+ '2102323234',
+ '+302646041461',
+ '120000000',
+ '20000000000',
+ '68129485729',
+ '6589394827',
+ '298RI89572',
+ '6924567890',
+ '6964567890',
+ '6844567890',
+ '690456789',
+ '00690456789',
+ 'not a number',
+ ],
+ },
+ {
+ locale: 'el-CY',
+ valid: [
+ '96546247',
+ '96978927',
+ '+35799837145',
+ '+35799646792',
+ '96056927',
+ '99629593',
+ '99849980',
+ '3599701619',
+ '+3599148725',
+ '96537247',
+ '3596676533',
+ '+35795123455',
+ '+35797012204',
+ '35799123456',
+ '+35794123456',
+ '+35796123456',
+ ],
+ invalid: [
+ '',
+ 'somechars',
+ '9697892',
+ '998499803',
+ '33799837145',
+ '+3799646792',
+ '93056927',
+ ],
+ },
+ {
+ locale: 'en-GB',
+ valid: [
+ '447789345856',
+ '+447861235675',
+ '07888814488',
+ ],
+ invalid: [
+ '67699567',
+ '0773894868',
+ '077389f8688',
+ '+07888814488',
+ '0152456999',
+ '442073456754',
+ '+443003434751',
+ '05073456754',
+ '08001123123',
+ '07043425232',
+ '01273884231',
+ '03332654034',
+ ],
+ },
+ {
+ locale: 'en-SG',
+ valid: [
+ '32891278',
+ '87654321',
+ '98765432',
+ '+6587654321',
+ '+6598765432',
+ '+6565241234',
+ ],
+ invalid: [
+ '332891231',
+ '987654321',
+ '876543219',
+ '8765432',
+ '9876543',
+ '12345678',
+ '+98765432',
+ '+9876543212',
+ '+15673628910',
+ '19876543210',
+ '8005552222',
+ ],
+ },
+ {
+ locale: 'en-US',
+ valid: [
+ '19876543210',
+ '8005552222',
+ '+15673628910',
+ '+1(567)3628910',
+ '+1(567)362-8910',
+ '+1(567) 362-8910',
+ '1(567)362-8910',
+ '1(567)362 8910',
+ '223-456-7890',
+ ],
+ invalid: [
+ '564785',
+ '0123456789',
+ '1437439210',
+ '+10345672645',
+ '11435213543',
+ '1(067)362-8910',
+ '1(167)362-8910',
+ '+2(267)362-8910',
+ '+3365520145',
+ ],
+ },
+ {
+ locale: 'en-CA',
+ valid: ['19876543210', '8005552222', '+15673628910'],
+ invalid: [
+ '564785',
+ '0123456789',
+ '1437439210',
+ '+10345672645',
+ '11435213543',
+ ],
+ },
+ {
+ locale: 'en-ZM',
+ valid: [
+ '0956684590',
+ '0966684590',
+ '0976684590',
+ '+260956684590',
+ '+260966684590',
+ '+260976684590',
+ '260976684590',
+ '+260779493521',
+ '+260760010936',
+ ],
+ invalid: [
+ '12345',
+ '',
+ 'Vml2YW11cyBmZXJtZtesting123',
+ '010-38238383',
+ '966684590',
+ '760010936',
+ ],
+ },
+ {
+ locale: ['en-ZW'],
+ valid: [
+ '+263561890123',
+ '+263715558041',
+ '+263775551112',
+ '+263775551695',
+ '+263715556633',
+ ],
+ invalid: [
+ '12345',
+ '',
+ 'Vml2YW11cyBmZXJtZtesting123',
+ '+2631234567890',
+ '+2641234567',
+ '+263981234',
+ '4736338855',
+ '66338855',
+ ],
+ },
+ {
+ locale: ['en-NA'],
+ valid: [
+ '+26466189012',
+ '+26461555804',
+ '+26461434221',
+ '+26487555169',
+ '+26481555663',
+ ],
+ invalid: [
+ '12345',
+ '',
+ 'Vml2YW11cyBmZXJtZtesting123',
+ '+2641234567890',
+ '+2641234567',
+ '+2648143422',
+ '+264981234',
+ '4736338855',
+ '66338855',
+ ],
+ },
+ {
+ locale: 'ru-RU',
+ valid: [
+ '+79676338855',
+ '79676338855',
+ '89676338855',
+ '9676338855',
+ ],
+ invalid: [
+ '12345',
+ '',
+ 'Vml2YW11cyBmZXJtZtesting123',
+ '010-38238383',
+ '+9676338855',
+ '19676338855',
+ '6676338855',
+ '+99676338855',
+ ],
+ },
+ {
+ locale: 'si-LK',
+ valid: [
+ '+94766661206',
+ '94713114340',
+ '0786642116',
+ '078 7642116',
+ '078-7642116',
+ '0749994567',
+ ],
+ invalid: [
+ '9912349956789',
+ '12345',
+ '1678123456',
+ '0731234567',
+ '0797878674',
+ ],
+ },
+ {
+ locale: 'sr-RS',
+ valid: [
+ '0640133338',
+ '063333133',
+ '0668888878',
+ '+381645678912',
+ '+381611314000',
+ '0655885010',
+ ],
+ invalid: [
+ '12345',
+ '',
+ 'Vml2YW11cyBmZXJtZtesting123',
+ '010-38238383',
+ '+9676338855',
+ '19676338855',
+ '6676338855',
+ '+99676338855',
+ ],
+ },
+ {
+ locale: 'en-NZ',
+ valid: [
+ '+6427987035',
+ '642240512347',
+ '0293981646',
+ '029968425',
+ ],
+ invalid: [
+ '12345',
+ '',
+ 'Vml2YW11cyBmZXJtZtesting123',
+ '+642956696123566',
+ '+02119620856',
+ '+9676338855',
+ '19676338855',
+ '6676338855',
+ '+99676338855',
+ ],
+ },
+ {
+ locale: 'en-MU',
+ valid: [
+ '+23012341234',
+ '12341234',
+ '012341234',
+ ],
+ invalid: [
+ '41234',
+ '',
+ '+230',
+ '+2301',
+ '+23012',
+ '+230123',
+ '+2301234',
+ '+23012341',
+ '+230123412',
+ '+2301234123',
+ '+230123412341',
+ '+2301234123412',
+ '+23012341234123',
+ ],
+ },
+ {
+ locale: ['nb-NO', 'nn-NO'], // for multiple locales
+ valid: [
+ '+4796338855',
+ '+4746338855',
+ '4796338855',
+ '4746338855',
+ '46338855',
+ '96338855',
+ ],
+ invalid: [
+ '12345',
+ '',
+ 'Vml2YW11cyBmZXJtZtesting123',
+ '+4676338855',
+ '19676338855',
+ '+4726338855',
+ '4736338855',
+ '66338855',
+ ],
+ },
+ {
+ locale: ['ne-NP'],
+ valid: [
+ '+9779817385479',
+ '+9779717385478',
+ '+9779862002615',
+ '+9779853660020',
+ ],
+ invalid: [
+ '12345',
+ '',
+ 'Vml2YW11cyBmZXJtZtesting123',
+ '+97796123456789',
+ '+9771234567',
+ '+977981234',
+ '4736338855',
+ '66338855',
+ ],
+ },
+ {
+ locale: 'vi-VN',
+ valid: [
+ '0336012403',
+ '+84586012403',
+ '84981577798',
+ '0708001240',
+ '84813601243',
+ '0523803765',
+ '0863803732',
+ '0883805866',
+ '0892405867',
+ '+84888696413',
+ '0878123456',
+ '84781234567',
+ '0553803765',
+ ],
+ invalid: [
+ '12345',
+ '',
+ 'Vml2YW11cyBmZXJtZtesting123',
+ '010-38238383',
+ '260976684590',
+ '01678912345',
+ '+841698765432',
+ '841626543219',
+ '0533803765',
+ '08712345678',
+ '+0321234567',
+ ],
+ },
+ {
+ locale: 'es-AR',
+ valid: [
+ '5491143214321',
+ '+5491143214321',
+ '+5492414321432',
+ '5498418432143',
+ ],
+ invalid: [
+ '1143214321',
+ '91143214321',
+ '+91143214321',
+ '549841004321432',
+ '549 11 43214321',
+ '549111543214321',
+ '5714003425432',
+ '549114a214321',
+ '54 9 11 4321-4321',
+ ],
+ },
+ {
+ locale: 'es-CO',
+ valid: [
+ '+573003321235',
+ '573003321235',
+ '3003321235',
+ '3213321235',
+ '3103321235',
+ '3243321235',
+ '573011140876',
+ ],
+ invalid: [
+ '1234',
+ '+57443875615',
+ '57309875615',
+ '57109834567',
+ '5792434567',
+ '5702345689',
+ '5714003425432',
+ '5703013347567',
+ '069834567',
+ '969834567',
+ '579871235',
+ '574321235',
+ '5784321235',
+ '5784321235',
+ '9821235',
+ '0698345',
+ '3321235',
+ ],
+ },
+ {
+ locale: 'es-CL',
+ valid: [
+ '+56733875615',
+ '56928590234',
+ '0928590294',
+ '0208590294',
+ ],
+ invalid: [
+ '1234',
+ '+5633875615',
+ '563875615',
+ '56109834567',
+ '56069834567',
+ ],
+ },
+ {
+ locale: 'es-EC',
+ valid: [
+ '+593987654321',
+ '593987654321',
+ '0987654321',
+ '027332615',
+ '+59323456789',
+ ],
+ invalid: [
+ '03321321',
+ '+593387561',
+ '59312345677',
+ '02344635',
+ '593123456789',
+ '081234567',
+ '+593912345678',
+ '+593902345678',
+ '+593287654321',
+ '593287654321',
+ ],
+ },
+ {
+ locale: 'es-CR',
+ valid: [
+ '+50688888888',
+ '+50665408090',
+ '+50640895069',
+ '25789563',
+ '85789563',
+ ],
+ invalid: [
+ '+5081',
+ '+5067777777',
+ '+50188888888',
+ '+50e987643254',
+ '+506e4t4',
+ '-50688888888',
+ '50688888888',
+ '12345678',
+ '98765432',
+ '01234567',
+ ],
+ },
+ {
+ locale: 'es-CU',
+ valid: [
+ '+5351234567',
+ '005353216547',
+ '51234567',
+ '53214567',
+ ],
+ invalid: [
+ '1234',
+ '+5341234567',
+ '0041234567',
+ '41234567',
+ '11234567',
+ '21234567',
+ '31234567',
+ '60303456',
+ '71234567',
+ '81234567',
+ '91234567',
+ '+5343216547',
+ '+5332165498',
+ '+53121234567',
+ '',
+ 'abc',
+ '+535123457',
+ '56043029304',
+ ],
+ },
+ {
+ locale: 'es-DO',
+ valid: [
+ '+18096622563',
+ '+18295614488',
+ '+18495259567',
+ '8492283478',
+ '8092324576',
+ '8292387713',
+ ],
+ invalid: [
+ '+18091',
+ '+1849777777',
+ '-18296643245',
+ '+18086643245',
+ '+18396643245',
+ '8196643245',
+ '+38492283478',
+ '6492283478',
+ '8192283478',
+ ],
+ },
+ {
+ locale: 'es-HN',
+ valid: [
+ '+50495551876',
+ '+50488908787',
+ '+50493456789',
+ '+50489234567',
+ '+50488987896',
+ '+50497567389',
+ '+50427367389',
+ '+50422357389',
+ '+50431257389',
+ '+50430157389',
+ ],
+ invalid: [
+ '12345',
+ '',
+ 'Vml2YW11cyBmZXJtZtesting123',
+ '+34683456543',
+ '65478932',
+ '+50298787654',
+ '+504989874',
+ ],
+ },
+ {
+ locale: 'es-ES',
+ valid: [
+ '+34654789321',
+ '654789321',
+ '+34714789321',
+ '714789321',
+ '+34744789321',
+ '744789321',
+ ],
+ invalid: [
+ '12345',
+ '',
+ 'Vml2YW11cyBmZXJtZtesting123',
+ '+3465478932',
+ '65478932',
+ '+346547893210',
+ '6547893210',
+ '+3470478932',
+ '7047893210',
+ '+34854789321',
+ '7547893219',
+ ],
+ },
+ {
+ locale: 'es-MX',
+ valid: [
+ '+52019654789321',
+ '+52199654789321',
+ '+5201965478932',
+ '+5219654789321',
+ '52019654789321',
+ '52199654789321',
+ '5201965478932',
+ '5219654789321',
+ '87654789321',
+ '8654789321',
+ '0187654789321',
+ '18654789321',
+ ],
+ invalid: [
+ '12345',
+ '',
+ 'Vml2YW11cyBmZXJtZtesting123',
+ '+3465478932',
+ '65478932',
+ '+346547893210',
+ '+34704789321',
+ '704789321',
+ '+34754789321',
+ ],
+ },
+ {
+ locale: 'es-NI',
+ valid: [
+ '+5051234567',
+ '+50512345678',
+ '5051234567',
+ '50512345678',
+ '+50555555555',
+ ],
+ invalid: [
+ '1234',
+ '',
+ '1234567',
+ '12345678',
+ '+12345678',
+ '+505123456789',
+ '+50612345678',
+ '+50712345678',
+ '-50512345678',
+ ],
+ },
+ {
+ locale: 'es-PA',
+ valid: [
+ '+5076784565',
+ '+5074321557',
+ '5073331112',
+ '+50723431212',
+ ],
+ invalid: [
+ '+50755555',
+ '+207123456',
+ '2001236542',
+ '+507987643254',
+ '+507jjjghtf',
+ ],
+ },
+ {
+ locale: 'es-PY',
+ valid: [
+ '+595991372649',
+ '+595992847352',
+ '+595993847593',
+ '+595994857473',
+ '+595995348532',
+ '+595996435231',
+ '+595981847362',
+ '+595982435452',
+ '+595983948502',
+ '+595984342351',
+ '+595985403481',
+ '+595986384012',
+ '+595971435231',
+ '+595972103924',
+ '+595973438542',
+ '+595974425864',
+ '+595975425843',
+ '+595976342546',
+ '+595961435234',
+ '+595963425043',
+ ],
+ invalid: [
+ '12345',
+ '',
+ 'Vml2YW11cyBmZXJtZtesting123',
+ '65478932',
+ '+59599384712',
+ '+5959938471234',
+ '+595547893218',
+ '+591993546843',
+ ],
+ },
+ {
+ locale: 'es-SV',
+ valid: [
+ '62136634',
+ '50361366631',
+ '+50361366634',
+ '+50361367217',
+ '+50361367460',
+ '+50371367632',
+ '+50371367767',
+ '+50371368314',
+ ],
+ invalid: [
+ '+5032136663',
+ '21346663',
+ '+50321366663',
+ '12345',
+ 'El salvador',
+ 'this should fail',
+ '+5032222',
+ '+503 1111 1111',
+ '00 +503 1234 5678',
+ ],
+ },
+ {
+ locale: 'es-UY',
+ valid: [
+ '+59899123456',
+ '099123456',
+ '+59894654321',
+ '091111111',
+ ],
+ invalid: [
+ '54321',
+ 'montevideo',
+ '',
+ '+598099123456',
+ '090883338',
+ '099 999 999',
+ ],
+ },
+ {
+ locale: 'es-VE',
+ valid: [
+ '+582125457765',
+ '+582125458053',
+ '+584125458053',
+ ],
+ invalid: [
+ '+585129934395',
+ '+58212993439',
+ '',
+ ],
+ },
+ {
+ locale: 'et-EE',
+ valid: [
+ '+372 512 34 567',
+ '372 512 34 567',
+ '+37251234567',
+ '51234567',
+ '81234567',
+ '+372842345678',
+ ],
+ invalid: [
+ '12345',
+ '',
+ 'NotANumber',
+ '+333 51234567',
+ '61234567',
+ '+51234567',
+ '+372 539 57 4',
+ '+372 900 1234',
+ '12345678',
+ ],
+ },
+ {
+ locale: 'pl-PL',
+ valid: [
+ '+48512689767',
+ '+48 56 376 87 47',
+ '56 566 78 46',
+ '657562855',
+ '+48657562855',
+ '+48 887472765',
+ '+48 56 6572724',
+ '+48 67 621 5461',
+ '48 67 621 5461',
+ '+48 45 621 5461',
+ ],
+ invalid: [
+ '+48 67 621 5461',
+ '+55657562855',
+ '3454535',
+ 'teststring',
+ '',
+ '1800-88-8687',
+ '+6019-5830837',
+ '357562855',
+ '+48 44 621 5461',
+ ],
+ },
+ {
+ locale: 'fa-IR',
+ valid: [
+ '+989123456789',
+ '989223456789',
+ '09323456789',
+ '09021456789',
+ '+98-990-345-6789',
+ '+98 938 345 6789',
+ '0938 345 6789',
+ ],
+ invalid: [
+ '',
+ '+989623456789',
+ '+981123456789',
+ '01234567890',
+ '09423456789',
+ '09823456789',
+ '9123456789',
+ '091234567890',
+ '0912345678',
+ '+98 912 3456 6789',
+ '0912 345 678',
+ ],
+ },
+ {
+ locale: 'fi-FI',
+ valid: [
+ '+358505557171',
+ '0455571',
+ '0505557171',
+ '358505557171',
+ '04412345',
+ '0457 123 45 67',
+ '+358457 123 45 67',
+ '+358 50 555 7171',
+ '0501234',
+ '+358501234',
+ '050 1234',
+ ],
+ invalid: [
+ '12345',
+ '',
+ '045557',
+ '045555717112312332423423421',
+ 'Vml2YW11cyBmZXJtZtesting123',
+ '010-38238383',
+ '+3-585-0555-7171',
+ '+9676338855',
+ '19676338855',
+ '6676338855',
+ '+99676338855',
+ '044123',
+ '019123456789012345678901',
+ ],
+ },
+ {
+ locale: 'fj-FJ',
+ valid: [
+ '+6799898679',
+ '6793788679',
+ '+679 989 8679',
+ '679 989 8679',
+ '679 3456799',
+ '679908 8909',
+ ],
+ invalid: [
+ '12345',
+ '',
+ '04555792',
+ '902w99900030900000000099',
+ '8uiuiuhhyy&GUU88d',
+ '010-38238383',
+ '19676338855',
+ '679 9 89 8679',
+ '6793 45679',
+ ],
+ },
+ {
+ locale: 'ms-MY',
+ valid: [
+ '+60128228789',
+ '+60195830837',
+ '+6019-5830837',
+ '+6019-5830837',
+ '+6010-4357675',
+ '+60172012370',
+ '0128737867',
+ '0172012370',
+ '01468987837',
+ '01112347345',
+ '016-2838768',
+ '016 2838768',
+ ],
+ invalid: [
+ '12345',
+ '601238788657',
+ '088387675',
+ '16-2838768',
+ '032551433',
+ '6088-387888',
+ '088-261987',
+ '1800-88-8687',
+ '088-320000',
+ '+01112353576',
+ '+0111419752',
+ ],
+ },
+ {
+ locale: 'fr-CM',
+ valid: [
+ '+237677936141',
+ '237623456789',
+ '+237698124842',
+ '237693029202',
+ ],
+ invalid: [
+ 'NotANumber',
+ '+(703)-572-2920',
+ '+237 623 45 67 890',
+ '+2379981247429',
+ ],
+ },
+ {
+ locale: 'ko-KR',
+ valid: [
+ '+82-010-1234-5678',
+ '+82-10-1234-5678',
+ '82-010-1234-5678',
+ '82-10-1234-5678',
+ '+82 10 1234 5678',
+ '010-123-5678',
+ '10-1234-5678',
+ '+82 10 1234 5678',
+ '011 1234 5678',
+ '+820112345678',
+ '01012345678',
+ '+82 016 1234 5678',
+ '82 19 1234 5678',
+ '+82 010 12345678',
+ ],
+ invalid: [
+ 'abcdefghi',
+ '+82 10 1234 567',
+ '+82 10o 1234 1234',
+ '+82 101 1234 5678',
+ '+82 10 12 5678',
+ '+011 7766 1234',
+ '011_7766_1234',
+ '+820 11 7766 1234',
+ ],
+ },
+ {
+ locale: 'ky-KG',
+ valid: [
+ '+996553033300',
+ '+996 222 123456',
+ '+996 500 987654',
+ '+996 555 111222',
+ '+996 700 333444',
+ '+996 770 555666',
+ '+996 880 777888',
+ '+996 990 999000',
+ '+996 995 555666',
+ '+996 996 555666',
+ '+996 997 555666',
+ '+996 998 555666',
+ ],
+ invalid: [
+ '+996 201 123456',
+ '+996 312 123456',
+ '+996 3960 12345',
+ '+996 3961 12345',
+ '+996 3962 12345',
+ '+996 3963 12345',
+ '+996 3964 12345',
+ '+996 3965 12345',
+ '+996 3966 12345',
+ '+996 3967 12345',
+ '+996 3968 12345',
+ '+996 511 123456',
+ '+996 522 123456',
+ '+996 561 123456',
+ '+996 571 123456',
+ '+996 624 123456',
+ '+996 623 123456',
+ '+996 622 123456',
+ '+996 609 123456',
+ '+996 100 12345',
+ '+996 100 1234567',
+ '996 100 123456',
+ '0 100 123456',
+ '0 100 123abc',
+ ],
+ },
+ {
+ locale: 'ja-JP',
+ valid: [
+ '09012345678',
+ '08012345678',
+ '07012345678',
+ '06012345678',
+ '090 1234 5678',
+ '+8190-1234-5678',
+ '+81 (0)90-1234-5678',
+ '+819012345678',
+ '+81-(0)90-1234-5678',
+ '+81 90 1234 5678',
+ ],
+ invalid: [
+ '12345',
+ '',
+ '045555717112312332423423421',
+ 'Vml2YW11cyBmZXJtZtesting123',
+ '+3-585-0555-7171',
+ '0 1234 5689',
+ '16 1234 5689',
+ '03_1234_5689',
+ '0312345678',
+ '0721234567',
+ '06 1234 5678',
+ '072 123 4567',
+ '0729 12 3456',
+ '07296 1 2345',
+ '072961 2345',
+ '03-1234-5678',
+ '+81312345678',
+ '+816-1234-5678',
+ '+81 090 1234 5678',
+ '+8109012345678',
+ '+81-090-1234-5678',
+ '90 1234 5678',
+ ],
+ },
+ {
+ locale: 'ir-IR',
+ valid: [
+ '09023818688',
+ '09123809999',
+ '+989023818688',
+ '+989103923523',
+ ],
+ invalid: [
+ '19023818688',
+ '323254',
+ '+903232323257',
+ '++3567868',
+ '0902381888832',
+ ],
+ },
+ {
+ locale: 'it-IT',
+ valid: [
+ '370 3175423',
+ '333202925',
+ '+39 310 7688449',
+ '+39 3339847632',
+ ],
+ invalid: [
+ '011 7387545',
+ '12345',
+ '+45 345 6782395',
+ ],
+ },
+ {
+ locale: 'fr-BE',
+ valid: [
+ '0470123456',
+ '+32470123456',
+ '32470123456',
+ '0421234567',
+ '+32421234567',
+ '32421234567',
+ ],
+ invalid: [
+ '12345',
+ '+3212345',
+ '3212345',
+ '04701234567',
+ '+3204701234567',
+ '3204701234567',
+ '0212345678',
+ '+320212345678',
+ '320212345678',
+ '021234567',
+ '+3221234567',
+ '3221234567',
+ ],
+ },
+ {
+ locale: 'nl-BE',
+ valid: [
+ '0470123456',
+ '+32470123456',
+ '32470123456',
+ '0421234567',
+ '+32421234567',
+ '32421234567',
+ ],
+ invalid: [
+ '12345',
+ '+3212345',
+ '3212345',
+ '04701234567',
+ '+3204701234567',
+ '3204701234567',
+ '0212345678',
+ '+320212345678',
+ '320212345678',
+ '021234567',
+ '+3221234567',
+ '3221234567',
+ ],
+ },
+ {
+ locale: 'nl-NL',
+ valid: [
+ '0670123456',
+ '0612345678',
+ '31612345678',
+ '31670123456',
+ '+31612345678',
+ '+31670123456',
+ '+31(0)612345678',
+ '0031612345678',
+ '0031(0)612345678',
+ ],
+ invalid: [
+ '12345',
+ '+3112345',
+ '3112345',
+ '06701234567',
+ '012345678',
+ '+3104701234567',
+ '3104701234567',
+ '0212345678',
+ '021234567',
+ '+3121234567',
+ '3121234567',
+ '+310212345678',
+ '310212345678',
+ ],
+ },
+ {
+ locale: 'nl-AW',
+ valid: [
+ '2975612345',
+ '2976412345',
+ '+2975612345',
+ '+2975912345',
+ '+2976412345',
+ '+2977312345',
+ '+2977412345',
+ '+2979912345',
+ ],
+ invalid: [
+ '12345',
+ '+2972345',
+ '2972345',
+ '06701234567',
+ '012345678',
+ '+2974701234567',
+ '2974701234567',
+ '0297345678',
+ '029734567',
+ '+2971234567',
+ '2971234567',
+ '+297212345678',
+ '297212345678',
+ 'number',
+ ],
+ },
+ {
+ locale: 'ro-MD',
+ valid: [
+ '+37360375781',
+ '+37361945673',
+ '+37362387563',
+ '+37368447788',
+ '+37369000101',
+ '+37367568910',
+ '+37376758294',
+ '+37378457892',
+ '+37379067436',
+ '37362387563',
+ '37368447788',
+ '37369000101',
+ '37367568910',
+ ],
+ invalid: [
+ '',
+ '+37363373381',
+ '+37364310581',
+ '+37365578199',
+ '+37371088636',
+ 'Vml2YW11cyBmZXJtZtesting123',
+ '123456',
+ '740123456',
+ '+40640123456',
+ '+40210123456',
+ ],
+ },
+ {
+ locale: 'ro-RO',
+ valid: [
+ '+40740123456',
+ '+40 740123456',
+ '+40740 123 456',
+ '+40740.123.456',
+ '+40740-123-456',
+ '40740123456',
+ '40 740123456',
+ '40740 123 456',
+ '40740.123.456',
+ '40740-123-456',
+ '0740123456',
+ '0740/123456',
+ '0740 123 456',
+ '0740.123.456',
+ '0740-123-456',
+ ],
+ invalid: [
+ '',
+ 'Vml2YW11cyBmZXJtZtesting123',
+ '123456',
+ '740123456',
+ '+40640123456',
+ '+40210123456',
+ '+0765351689',
+ '+0711419752',
+ ],
+ },
+ {
+ locale: 'id-ID',
+ valid: [
+ '0811 778 998',
+ '0811 7785 9983',
+ '0812 7784 9984',
+ '0813 7782 9982',
+ '0821 1234 1234',
+ '0822 1234 1234',
+ '0823 1234 1234',
+ '0852 1234 6764',
+ '0853 1234 6764',
+ '0851 1234 6764',
+ '0814 7782 9982',
+ '0815 7782 9982',
+ '0816 7782 9982',
+ '0855 7782 9982',
+ '0856 7782 9982',
+ '0857 7782 9982',
+ '0858 7782 9982',
+ '0817 7785 9983',
+ '0818 7784 9984',
+ '0819 7782 9982',
+ '0859 1234 1234',
+ '0877 1234 1234',
+ '0878 1234 1234',
+ '0895 7785 9983',
+ '0896 7784 9984',
+ '0897 7782 9982',
+ '0898 1234 1234',
+ '0899 1234 1234',
+ '0881 7785 9983',
+ '0882 7784 9984',
+ '0883 7782 9982',
+ '0884 1234 1234',
+ '0886 1234 1234',
+ '0887 1234 1234',
+ '0888 7785 9983',
+ '0889 7784 9984',
+ '0828 7784 9984',
+ '0838 7784 9984',
+ '0831 7784 9984',
+ '0832 7784 9984',
+ '0833 7784 9984',
+ '089931236181900',
+ '62811 778 998',
+ '62811778998',
+ '628993123618190',
+ '62898 740123456',
+ '62899 7401 2346',
+ '+62811 778 998',
+ '+62811778998',
+ '+62812 9650 3508',
+ '08197231819',
+ '085361008008',
+ '+62811787391',
+ ],
+ invalid: [
+ '0899312361819001',
+ '0217123456',
+ '622178878890',
+ '6221 740123456',
+ '0341 8123456',
+ '0778 89800910',
+ '0741 123456',
+ '+6221740123456',
+ '+65740 123 456',
+ '',
+ 'ASDFGJKLmZXJtZtesting123',
+ '123456',
+ '740123456',
+ '+65640123456',
+ '+64210123456',
+ ],
+ },
+ {
+ locale: 'lt-LT',
+ valid: [
+ '+37051234567',
+ '851234567',
+ ],
+ invalid: [
+ '+65740 123 456',
+ '',
+ 'ASDFGJKLmZXJtZtesting123',
+ '123456',
+ '740123456',
+ '+65640123456',
+ '+64210123456',
+ ],
+ },
+ {
+ locale: 'uk-UA',
+ valid: [
+ '+380501234567',
+ '+380631234567',
+ '+380661234567',
+ '+380671234567',
+ '+380681234567',
+ '+380731234567',
+ '+380751234567',
+ '+380771234567',
+ '+380911234567',
+ '+380921234567',
+ '+380931234567',
+ '+380941234567',
+ '+380951234567',
+ '+380961234567',
+ '+380971234567',
+ '+380981234567',
+ '+380991234567',
+ '380501234567',
+ '380631234567',
+ '380661234567',
+ '380671234567',
+ '380681234567',
+ '380731234567',
+ '380751234567',
+ '380771234567',
+ '380911234567',
+ '380921234567',
+ '380931234567',
+ '380941234567',
+ '380951234567',
+ '380961234567',
+ '380971234567',
+ '380981234567',
+ '380991234567',
+ '0501234567',
+ '0631234567',
+ '0661234567',
+ '0671234567',
+ '0681234567',
+ '0731234567',
+ '0751234567',
+ '0771234567',
+ '0911234567',
+ '0921234567',
+ '0931234567',
+ '0941234567',
+ '0951234567',
+ '0961234567',
+ '0971234567',
+ '0981234567',
+ '0991234567',
+ ],
+ invalid: [
+ '+30982345679',
+ '+380321234567',
+ '+380441234567',
+ '982345679',
+ '80982345679',
+ '+380 98 234 5679',
+ '+380-98-234-5679',
+ '+380 (98) 234-56-79',
+ '',
+ 'ASDFGJKLmZXJtZtesting123',
+ '123456',
+ '740123456',
+ ],
+ },
+ {
+ locale: 'uz-UZ',
+ valid: [
+ '+998664835244',
+ '998664835244',
+ '664835244',
+ '+998957124555',
+ '998957124555',
+ '957124555',
+ ],
+ invalid: [
+ '+998644835244',
+ '998644835244',
+ '644835244',
+ '+99664835244',
+ 'ASDFGJKLmZXJtZtesting123',
+ '123456789',
+ '870123456',
+ '',
+ '+998',
+ '998',
+ ],
+ },
+ {
+ locale: 'da-DK',
+ valid: [
+ '12345678',
+ '12 34 56 78',
+ '45 12345678',
+ '4512345678',
+ '45 12 34 56 78',
+ '+45 12 34 56 78',
+ ],
+ invalid: [
+ '',
+ '+45010203',
+ 'ASDFGJKLmZXJtZtesting123',
+ '123456',
+ '12 34 56',
+ '123 123 12',
+ ],
+ },
+ {
+ locale: 'sv-SE',
+ valid: [
+ '+46701234567',
+ '46701234567',
+ '0721234567',
+ '073-1234567',
+ '0761-234567',
+ '079-123 45 67',
+ ],
+ invalid: [
+ '12345',
+ '+4670123456',
+ '+46301234567',
+ '+0731234567',
+ '0731234 56',
+ '+7312345678',
+ '',
+ ],
+ },
+ {
+ locale: 'fo-FO',
+ valid: [
+ '123456',
+ '12 34 56',
+ '298 123456',
+ '298123456',
+ '298 12 34 56',
+ '+298 12 34 56',
+ ],
+ invalid: [
+ '',
+ '+4501020304',
+ 'ASDFGJKLmZXJtZtesting123',
+ '12345678',
+ '12 34 56 78',
+ ],
+ },
+ {
+ locale: 'kl-GL',
+ valid: [
+ '123456',
+ '12 34 56',
+ '299 123456',
+ '299123456',
+ '299 12 34 56',
+ '+299 12 34 56',
+ ],
+ invalid: [
+ '',
+ '+4501020304',
+ 'ASDFGJKLmZXJtZtesting123',
+ '12345678',
+ '12 34 56 78',
+ ],
+ },
+ {
+ locale: 'kk-KZ',
+ valid: [
+ '+77254716212',
+ '77254716212',
+ '87254716212',
+ '7254716212',
+ ],
+ invalid: [
+ '12345',
+ '',
+ 'ASDFGJKLmZXJtZtesting123',
+ '010-38238383',
+ '+9676338855',
+ '19676338855',
+ '6676338855',
+ '+99676338855',
+ ],
+ },
+ {
+ locale: 'be-BY',
+ valid: [
+ '+375241234567',
+ '+375251234567',
+ '+375291234567',
+ '+375331234567',
+ '+375441234567',
+ '375331234567',
+ ],
+ invalid: [
+ '12345',
+ '',
+ 'ASDFGJKLmZXJtZtesting123',
+ '010-38238383',
+ '+9676338855',
+ '19676338855',
+ '6676338855',
+ '+99676338855',
+ ],
+ },
+ {
+ locale: 'th-TH',
+ valid: [
+ '0912345678',
+ '+66912345678',
+ '66912345678',
+ ],
+ invalid: [
+ '99123456789',
+ '12345',
+ '67812345623',
+ '081234567891',
+ ],
+ },
+ {
+ locale: 'tk-TM',
+ valid: [
+ '+99312495154',
+ '99312130136',
+ '+99312918407',
+ '99312183399',
+ '812391717',
+ ],
+ invalid: [
+ '12345',
+ '+99412495154',
+ '99412495154',
+ '998900066506',
+ ],
+ },
+ {
+ locale: 'en-SL',
+ valid: [
+ '+23274560591',
+ '23274560591',
+ '074560591',
+ ],
+ invalid: [
+ '0745605912',
+ '12345',
+ '232745605917',
+ '0797878674',
+ '23274560591 ',
+ ],
+ },
+ {
+ locale: 'en-BW',
+ valid: [
+ '+26772868545',
+ '+26776368790',
+ '+26774560512',
+ '26774560591',
+ '26778560512',
+ '74560512',
+ '76710284',
+ ],
+ invalid: [
+ '0799375902',
+ '12345',
+ '+2670745605448',
+ '2670745605482',
+ '+26779685451',
+ '+26770685451',
+ '267074560',
+ '2670ab5608',
+ '+267074560',
+ '70560512',
+ '79710284',
+ ],
+ },
+ {
+ locale: 'az-AZ',
+ valid: [
+ '+994707007070',
+ '0707007070',
+ '+994502111111',
+ '0505436743',
+ '0554328772',
+ '0104328772',
+ '0993301022',
+ '+994776007139',
+ '+994106007139',
+ ],
+ invalid: [
+ 'wrong-number',
+ '',
+ '994707007070',
+ '++9945005050',
+ '556007070',
+ '1234566',
+ '+994778008080a',
+ ],
+ },
+ {
+ locale: 'de-LU',
+ valid: [
+ '601123456',
+ '+352601123456',
+ ],
+ invalid: [
+ 'NaN',
+ '791234',
+ '+352791234',
+ '26791234',
+ '+35226791234',
+ '+112039812',
+ '+352703123456',
+ '1234',
+ ],
+ },
+ {
+ locale: 'it-SM',
+ valid: [
+ '612345',
+ '05496123456',
+ '+37861234567',
+ '+390549612345678',
+ '+37805496123456789',
+ ],
+ invalid: [
+ '61234567890',
+ '6123',
+ '1234567',
+ '+49123456',
+ 'NotANumber',
+ ],
+ },
+ {
+ locale: 'so-SO',
+ valid: [
+ '+252601234567',
+ '+252650101010',
+ '+252794567120',
+ '252650647388',
+ '252751234567',
+ '0601234567',
+ '0609876543',
+ ],
+ invalid: [
+ '',
+ 'not a number',
+ '+2526012345678',
+ '25260123456',
+ '+252705555555',
+ '+0601234567',
+ '06945454545',
+ ],
+ },
+ {
+ locale: 'sq-AL',
+ valid: [
+ '0621234567',
+ '0661234567',
+ '0671234567',
+ '0681234567',
+ '0691234567',
+ '+355621234567',
+ '+355651234567',
+ '+355661234567',
+ '+355671234567',
+ '+355681234567',
+ '+355691234567',
+ ],
+ invalid: [
+ '67123456',
+ '06712345',
+ '067123456',
+ '06712345678',
+ '0571234567',
+ '+3556712345',
+ '+35565123456',
+ '+35157123456',
+ 'NotANumber',
+ ],
+ },
+ {
+ locale: 'ca-AD',
+ valid: [
+ '+376312345',
+ '312345',
+ ],
+ invalid: [
+ '31234',
+ '31234567',
+ '512345',
+ 'NotANumber',
+ ],
+ },
+ {
+ locale: 'pt-AO',
+ valid: [
+ '+244911123432',
+ '911123432',
+ '244911123432',
+ ],
+ invalid: [
+ '+2449111234321',
+ '+244811123432',
+ '31234',
+ '31234567',
+ '512345',
+ 'NotANumber',
+ ],
+ },
+ {
+ locale: 'lv-LV',
+ valid: [
+ '+37121234567',
+ '37121234567',
+ ],
+ invalid: [
+ '+37201234567',
+ '+3754321',
+ '3712123456',
+ '+371212345678',
+ 'NotANumber',
+ ],
+ },
+ {
+ locale: 'mg-MG',
+ valid: [
+ '+261204269174',
+ '261204269174',
+ '0204269174',
+ '0209269174',
+ '0374269174',
+ '4269174',
+ ],
+ invalid: [
+ '0261204269174',
+ '+261 20 4 269174',
+ '+261 20 4269174',
+ '020 4269174',
+ '204269174',
+ '0404269174',
+ 'NotANumber',
+ ],
+ },
+ {
+ locale: 'mn-MN',
+ valid: [
+ '+97699112222',
+ '97696112222',
+ '97695112222',
+ '01197691112222',
+ '0097688112222',
+ '+97677112222',
+ '+97694112222',
+ '+97681112222',
+ ],
+ invalid: [
+ '+97888112222',
+ '+97977112222',
+ '+97094112222',
+ '+97281112222',
+ '02297681112222',
+ ],
+ },
+ {
+ locale: 'my-MM',
+ valid: [
+ '+959750202595',
+ '09750202595',
+ '9750202595',
+ '+959260000966',
+ '09256000323',
+ '09276000323',
+ '09426000323',
+ '09456000323',
+ '09761234567',
+ '09791234567',
+ '09961234567',
+ '09771234567',
+ '09660000234',
+ ],
+ invalid: [
+ '59750202595',
+ '+9597502025',
+ '08943234524',
+ '09950000966',
+ '959240000966',
+ '09246000323',
+ '09466000323',
+ '09951234567',
+ '09801234567',
+ '09650000234',
+ ],
+ },
+ {
+ locale: 'en-PG',
+ valid: [
+ '+67570123456',
+ '67570123456',
+ '+67571123456',
+ '+67572123456',
+ '+67573123456',
+ '+67574123456',
+ '+67575123456',
+ '+67576123456',
+ '+67577123456',
+ '+67578123456',
+ '+67579123456',
+ '+67581123456',
+ '+67588123456',
+ ],
+ invalid: [
+ '',
+ 'not a number',
+ '12345',
+ '+675123456789',
+ '+67580123456',
+ '+67569123456',
+ '+67582123456',
+ '+6757012345',
+ ],
+ },
+ {
+ locale: 'en-AG',
+ valid: [
+ '12687151234',
+ '+12687151234',
+ '+12684641234',
+ '12684641234',
+ '+12687211234',
+ '+12687302468',
+ '+12687642456',
+ '+12687763333',
+ ],
+ invalid: [
+ '2687151234',
+ '+12687773333',
+ '+126846412333',
+ '+12684641',
+ '+12687123456',
+ '+12687633456',
+ ],
+ },
+ {
+ locale: 'en-AI',
+ valid: [
+ '+12642351234',
+ '12642351234',
+ '+12644612222',
+ '+12645366326',
+ '+12645376326',
+ '+12647246326',
+ '+12647726326',
+ ],
+ invalid: [
+ '',
+ 'not a number',
+ '+22642351234',
+ '+12902351234',
+ '+12642331234',
+ '+1264235',
+ '22642353456',
+ '+12352643456',
+ ],
+ },
+ {
+ locale: 'en-KN',
+ valid: [
+ '+18694699040',
+ '18694699040',
+ '+18697652917',
+ '18697652917',
+ '18694658472',
+ '+18696622969',
+ '+18694882224',
+ ],
+ invalid: [
+ '',
+ '+18694238545',
+ '+1 8694882224',
+ '8694658472',
+ '+186946990',
+ '+1869469904',
+ '1869469904',
+ ],
+ },
+ {
+ locale: 'en-PK',
+ valid: [
+ '+923412877421',
+ '+923001234567',
+ '00923001234567',
+ '923001234567',
+ '03001234567',
+ ],
+ invalid: [
+ '+3001234567',
+ '+933001234567',
+ '+924001234567',
+ '+92300123456720',
+ '030012345672',
+ '30012345673',
+ '0030012345673',
+ '3001234567',
+ ],
+ },
+ {
+ locale: ['tg-TJ'],
+ valid: [
+ '+992553388551',
+ '+992553322551',
+ '992553388551',
+ '992553322551',
+ ],
+ invalid: [
+ '12345',
+ '',
+ 'Vml2YW11cyBmZXJtZtesting123',
+ '+995563388559',
+ '+9955633559',
+ '19676338855',
+ '+992263388505',
+ '9923633885',
+ '99255363885',
+ '66338855',
+ ],
+ },
+ {
+ locale: 'dv-MV',
+ valid: [
+ '+9609112345',
+ '+9609958973',
+ '+9607258963',
+ '+9607958463',
+ '9609112345',
+ '9609958973',
+ '9607212963',
+ '9607986963',
+ '9112345',
+ '9958973',
+ '7258963',
+ '7958963',
+ ],
+ invalid: [
+ '+96059234567',
+ '+96045789',
+ '7812463784',
+ 'NotANumber',
+ '+9607112345',
+ '+9609012345',
+ '+609012345',
+ '+96071123456',
+ '3412345',
+ '9603412345',
+ ],
+ },
+ {
+ locale: 'ar-YE',
+ valid: [
+ '737198225',
+ '733111355',
+ '+967700990270',
+ ],
+ invalid: [
+ '+5032136663',
+ '21346663',
+ '+50321366663',
+ '12345',
+ 'Yemen',
+ 'this should fail',
+ '+5032222',
+ '+503 1111 1111',
+ '00 +503 1234 5678',
+ ],
+ },
+ {
+ locale: 'ar-EH',
+ valid: [
+ '+212-5288-12312',
+ '+212-5288 12312',
+ '+212 5288 12312',
+ '212528912312',
+ '+212528912312',
+ '+212528812312',
+ ],
+ invalid: [
+ '212528812312123',
+ '+212-5290-12312',
+ '++212528812312',
+ '12345',
+ 'Wester Sahara',
+ 'this should fail',
+ '212 5288---12312',
+ '+503 1111 1111',
+ '00 +503 1234 5678',
+ ],
+ },
+ {
+ locale: 'fa-AF',
+ valid: [
+ '0511231231',
+ '+93511231231',
+ '+93281234567',
+ ],
+ invalid: [
+ '212528812312123',
+ '+212-5290-12312',
+ '++212528812312',
+ '12345',
+ 'Afghanistan',
+ 'this should fail',
+ '212 5288---12312',
+ '+503 1111 1111',
+ '00 +503 1234 5678',
+ ],
+ },
+ {
+ locale: 'en-SS',
+ valid: [
+ '+211928530422',
+ '+211913384561',
+ '+211972879174',
+ '+211952379334',
+ '0923346543',
+ '0950459022',
+ '0970934567',
+ '211979841238',
+ '211929843238',
+ '211959840238',
+ ],
+ invalid: [
+ '911',
+ '+211999',
+ '123456789909',
+ 'South Sudan',
+ '21195 840 238',
+ '+211981234567',
+ '+211931234567',
+ '+211901234567',
+ '+211991234567',
+
+ ],
+ },
+ {
+ locale: 'es-GT',
+ valid: [
+ '+50221234567',
+ '+50277654321',
+ '50226753421',
+ '50272332468',
+ '50278984455',
+ '+50273472492',
+ '71234567',
+ '21132398',
+ ],
+ invalid: [
+ '44',
+ '+5022712345678',
+ '1234567899',
+ '502712345678',
+ 'This should fail',
+ '5021931234567',
+ '+50281234567',
+ ],
+ },
+ {
+ locale: 'mk-MK',
+ valid: [
+ '+38923234567',
+ '38931234567',
+ '022123456',
+ '22234567',
+ '71234567',
+ '31234567',
+ '+38923091500',
+ '80091234',
+ '81123456',
+ '54123456',
+ ],
+ invalid: [
+ '38912345678',
+ '+389123456789',
+ '21234567',
+ '123456789',
+ '+3891234567',
+ '700012345',
+ '510123456',
+ 'This should fail',
+ '+389123456',
+ '389123456',
+ '80912345',
+ ],
+ },
+ {
+ locale: 'ar-QA',
+ valid: ['+97435551234', '+97455551234', '+97465551234', '+97475551234', '35551234', '55551234', '65551234', '75551234'],
+ invalid: ['+97445551234', '+97405551234', '+9745555123', '+974555512345', '+97355551234', '+9125551234', '25551234', '+13005551234', '45551234', '95551234', '+9745555abcd', '', '+974'],
+ },
+ ];
+
+ let allValid = [];
+
+ fixtures.forEach((fixture) => {
+ // to be used later on for validating 'any' locale
+ if (fixture.valid) allValid = allValid.concat(fixture.valid);
+
+ if (Array.isArray(fixture.locale)) {
+ test({
+ validator: 'isMobilePhone',
+ valid: fixture.valid,
+ invalid: fixture.invalid,
+ args: [fixture.locale],
+ });
+ } else {
+ test({
+ validator: 'isMobilePhone',
+ valid: fixture.valid,
+ invalid: fixture.invalid,
+ args: [fixture.locale],
+ });
+ }
+ });
+
+ test({
+ validator: 'isMobilePhone',
+ valid: allValid,
+ invalid: [
+ '',
+ 'asdf',
+ '1',
+ 'ASDFGJKLmZXJtZtesting123',
+ 'Vml2YW11cyBmZXJtZtesting123',
+ ],
+ args: ['any'],
+ });
+
+ // strict mode
+ test({
+ validator: 'isMobilePhone',
+ valid: [
+ '+254728530234',
+ '+299 12 34 56',
+ '+94766660206',
+ ],
+ invalid: [
+ '254728530234',
+ '0728530234',
+ '+728530234',
+ '766667206',
+ '0766670206',
+ ],
+ args: ['any', { strictMode: true }],
+ });
+
+ // falsey locale defaults to 'any'
+ test({
+ validator: 'isMobilePhone',
+ valid: allValid,
+ invalid: [
+ '',
+ 'asdf',
+ '1',
+ 'ASDFGJKLmZXJtZtesting123',
+ 'Vml2YW11cyBmZXJtZtesting123',
+ ],
+ args: [],
+ });
+ });
+
+ // de-CH, fr-CH, it-CH
+ test({
+ validator: 'isMobilePhone',
+ valid: [
+ '+41751112233',
+ '+41761112233',
+ '+41771112233',
+ '+41781112233',
+ '+41791112233',
+ '+411122112211',
+ ],
+ invalid: [
+ '+41041112233',
+ ],
+ args: [],
+ });
+
+
+ it('should error on invalid locale', () => {
+ test({
+ validator: 'isMobilePhone',
+ args: [{ locale: ['is-NOT'] }],
+ error: [
+ '+123456789',
+ '012345',
+ ],
+ });
+ });
+
+ it('should validate currency', () => {
+ // -$##,###.## (en-US, en-CA, en-AU, en-NZ, en-HK)
+ test({
+ validator: 'isCurrency',
+ valid: [
+ '-$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',
+ ],
+ invalid: [
+ '1.234',
+ '$1.1',
+ '$ 32.50',
+ '500$',
+ '.0001',
+ '$.001',
+ '$0.001',
+ '12,34.56',
+ '123456,123,123456',
+ '123,4',
+ ',123',
+ '$-,123',
+ '$',
+ '.',
+ ',',
+ '00',
+ '$-',
+ '$-,.',
+ '-',
+ '-$',
+ '',
+ '- $',
+ ],
+ });
+
+ // -$##,###.## (en-US, en-CA, en-AU, en-NZ, en-HK)
+ test({
+ validator: 'isCurrency',
+ args: [
+ {
+ allow_decimal: false,
+ },
+ ],
+ valid: [
+ '-$10,123',
+ '$10,123',
+ '$10123',
+ '10,123',
+ '10123',
+ '10,123',
+ '1,123,456',
+ '1123456',
+ '1',
+ '0',
+ '$0',
+ '-$0',
+ '$100,234,567',
+ '$10,123',
+ '10,123',
+ '-10123',
+ ],
+ invalid: [
+ '-$10,123.45',
+ '$10,123.45',
+ '$10123.45',
+ '10,123.45',
+ '10123.45',
+ '1.39',
+ '.03',
+ '0.10',
+ '$0.10',
+ '-$0.01',
+ '-$.99',
+ '$100,234,567.89',
+ '1.234',
+ '$1.1',
+ '$ 32.50',
+ '.0001',
+ '$.001',
+ '$0.001',
+ '12,34.56',
+ '123,4',
+ ',123',
+ '$-,123',
+ '$',
+ '.',
+ ',',
+ '00',
+ '$-',
+ '$-,.',
+ '-',
+ '-$',
+ '',
+ '- $',
+ ],
+ });
+
+ // -$##,###.## (en-US, en-CA, en-AU, en-NZ, en-HK)
+ test({
+ validator: 'isCurrency',
+ args: [
+ {
+ require_decimal: true,
+ },
+ ],
+ valid: [
+ '-$10,123.45',
+ '$10,123.45',
+ '$10123.45',
+ '10,123.45',
+ '10123.45',
+ '10,123.00',
+ '1.39',
+ '.03',
+ '0.10',
+ '$0.10',
+ '-$0.01',
+ '-$.99',
+ '$100,234,567.89',
+ ],
+ invalid: [
+ '$10,123',
+ '10,123',
+ '-10123',
+ '1,123,456',
+ '1123456',
+ '1.234',
+ '$1.1',
+ '$ 32.50',
+ '500$',
+ '.0001',
+ '$.001',
+ '$0.001',
+ '12,34.56',
+ '123456,123,123456',
+ '123,4',
+ ',123',
+ '$-,123',
+ '$',
+ '.',
+ ',',
+ '00',
+ '$-',
+ '$-,.',
+ '-',
+ '-$',
+ '',
+ '- $',
+ ],
+ });
+
+ // -$##,###.## (en-US, en-CA, en-AU, en-NZ, en-HK)
+ test({
+ validator: 'isCurrency',
+ args: [
+ {
+ digits_after_decimal: [1, 3],
+ },
+ ],
+ valid: [
+ '-$10,123.4',
+ '$10,123.454',
+ '$10123.452',
+ '10,123.453',
+ '10123.450',
+ '10,123',
+ '1,123,456',
+ '1123456',
+ '1.3',
+ '.030',
+ '0.100',
+ '$0.1',
+ '-$0.0',
+ '-$.9',
+ '$100,234,567.893',
+ '$10,123',
+ '10,123.123',
+ '-10123.1',
+ ],
+ invalid: [
+ '1.23',
+ '$1.13322',
+ '$ 32.50',
+ '500$',
+ '.0001',
+ '$.01',
+ '$0.01',
+ '12,34.56',
+ '123456,123,123456',
+ '123,4',
+ ',123',
+ '$-,123',
+ '$',
+ '.',
+ ',',
+ '00',
+ '$-',
+ '$-,.',
+ '-',
+ '-$',
+ '',
+ '- $',
+ ],
+ });
+
+ // -$##,###.## with $ required (en-US, en-CA, en-AU, en-NZ, en-HK)
+ test({
+ validator: 'isCurrency',
+ args: [
+ {
+ require_symbol: true,
+ },
+ ],
+ valid: [
+ '-$10,123.45',
+ '$10,123.45',
+ '$10123.45',
+ '$10,123.45',
+ '$10,123',
+ '$1,123,456',
+ '$1123456',
+ '$1.39',
+ '$.03',
+ '$0.10',
+ '$0.10',
+ '-$0.01',
+ '-$.99',
+ '$100,234,567.89',
+ '$10,123',
+ '-$10123',
+ ],
+ invalid: [
+ '1.234',
+ '$1.234',
+ '1.1',
+ '$1.1',
+ '$ 32.50',
+ ' 32.50',
+ '500',
+ '10,123,456',
+ '.0001',
+ '$.001',
+ '$0.001',
+ '1,234.56',
+ '123456,123,123456',
+ '$123456,123,123456',
+ '123.4',
+ '$123.4',
+ ',123',
+ '$,123',
+ '$-,123',
+ '$',
+ '.',
+ '$.',
+ ',',
+ '$,',
+ '00',
+ '$00',
+ '$-',
+ '$-,.',
+ '-',
+ '-$',
+ '',
+ '$ ',
+ '- $',
+ ],
+ });
+
+ // ¥-##,###.## (zh-CN)
+ test({
+ validator: 'isCurrency',
+ args: [
+ {
+ symbol: '¥',
+ negative_sign_before_digits: true,
+ },
+ ],
+ valid: [
+ '123,456.78',
+ '-123,456.78',
+ '¥6,954,231',
+ '¥-6,954,231',
+ '¥10.03',
+ '¥-10.03',
+ '10.03',
+ '1.39',
+ '.03',
+ '0.10',
+ '¥-10567.01',
+ '¥0.01',
+ '¥1,234,567.89',
+ '¥10,123',
+ '¥-10,123',
+ '¥-10,123.45',
+ '10,123',
+ '10123',
+ '¥-100',
+ ],
+ invalid: [
+ '1.234',
+ '¥1.1',
+ '5,00',
+ '.0001',
+ '¥.001',
+ '¥0.001',
+ '12,34.56',
+ '123456,123,123456',
+ '123 456',
+ ',123',
+ '¥-,123',
+ '',
+ ' ',
+ '¥',
+ '¥-',
+ '¥-,.',
+ '-',
+ '- ¥',
+ '-¥',
+ ],
+ });
+
+ test({
+ validator: 'isCurrency',
+ args: [
+ {
+ negative_sign_after_digits: true,
+ },
+ ],
+ valid: [
+ '$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-',
+ ],
+ invalid: [
+ '-123',
+ '1.234',
+ '$1.1',
+ '$ 32.50',
+ '500$',
+ '.0001',
+ '$.001',
+ '$0.001',
+ '12,34.56',
+ '123456,123,123456',
+ '123,4',
+ ',123',
+ '$-,123',
+ '$',
+ '.',
+ ',',
+ '00',
+ '$-',
+ '$-,.',
+ '-',
+ '-$',
+ '',
+ '- $',
+ ],
+ });
+
+ // ¥##,###.## with no negatives (zh-CN)
+ test({
+ validator: 'isCurrency',
+ args: [
+ {
+ symbol: '¥',
+ allow_negatives: false,
+ },
+ ],
+ valid: [
+ '123,456.78',
+ '¥6,954,231',
+ '¥10.03',
+ '10.03',
+ '1.39',
+ '.03',
+ '0.10',
+ '¥0.01',
+ '¥1,234,567.89',
+ '¥10,123',
+ '10,123',
+ '10123',
+ '¥100',
+ ],
+ invalid: [
+ '1.234',
+ '-123,456.78',
+ '¥-6,954,231',
+ '¥-10.03',
+ '¥-10567.01',
+ '¥1.1',
+ '¥-10,123',
+ '¥-10,123.45',
+ '5,00',
+ '¥-100',
+ '.0001',
+ '¥.001',
+ '¥-.001',
+ '¥0.001',
+ '12,34.56',
+ '123456,123,123456',
+ '123 456',
+ ',123',
+ '¥-,123',
+ '',
+ ' ',
+ '¥',
+ '¥-',
+ '¥-,.',
+ '-',
+ '- ¥',
+ '-¥',
+ ],
+ });
+
+ // R ## ###,## and R-10 123,25 (el-ZA)
+ test({
+ validator: 'isCurrency',
+ args: [
+ {
+ symbol: 'R',
+ negative_sign_before_digits: true,
+ thousands_separator: ' ',
+ decimal_separator: ',',
+ allow_negative_sign_placeholder: true,
+ },
+ ],
+ valid: [
+ '123 456,78',
+ '-10 123',
+ 'R-10 123',
+ 'R 6 954 231',
+ 'R10,03',
+ '10,03',
+ '1,39',
+ ',03',
+ '0,10',
+ 'R10567,01',
+ 'R0,01',
+ 'R1 234 567,89',
+ 'R10 123',
+ 'R 10 123',
+ 'R 10123',
+ 'R-10123',
+ '10 123',
+ '10123',
+ ],
+ invalid: [
+ '1,234',
+ 'R -10123',
+ 'R- 10123',
+ 'R,1',
+ ',0001',
+ 'R,001',
+ 'R0,001',
+ '12 34,56',
+ '123456 123 123456',
+ ' 123',
+ '- 123',
+ '123 ',
+ '',
+ ' ',
+ 'R',
+ 'R- .1',
+ 'R-',
+ '-',
+ '-R 10123',
+ 'R00',
+ 'R -',
+ '-R',
+ ],
+ });
+
+ // -€ ##.###,## (it-IT)
+ test({
+ validator: 'isCurrency',
+ args: [
+ {
+ symbol: '€',
+ thousands_separator: '.',
+ decimal_separator: ',',
+ allow_space_after_symbol: true,
+ },
+ ],
+ valid: [
+ '123.456,78',
+ '-123.456,78',
+ '€6.954.231',
+ '-€6.954.231',
+ '€ 896.954.231',
+ '-€ 896.954.231',
+ '16.954.231',
+ '-16.954.231',
+ '€10,03',
+ '-€10,03',
+ '10,03',
+ '-10,03',
+ '-1,39',
+ ',03',
+ '0,10',
+ '-€10567,01',
+ '-€ 10567,01',
+ '€ 0,01',
+ '€1.234.567,89',
+ '€10.123',
+ '10.123',
+ '-€10.123',
+ '€ 10.123',
+ '€10.123',
+ '€ 10123',
+ '10.123',
+ '-10123',
+ ],
+ invalid: [
+ '1,234',
+ '€ 1,1',
+ '50#,50',
+ '123,@€ ',
+ '€€500',
+ ',0001',
+ '€ ,001',
+ '€0,001',
+ '12.34,56',
+ '123456.123.123456',
+ '€123€',
+ '',
+ ' ',
+ '€',
+ ' €',
+ '€ ',
+ '€€',
+ ' 123',
+ '- 123',
+ '.123',
+ '-€.123',
+ '123 ',
+ '€-',
+ '- €',
+ '€ - ',
+ '-',
+ '- ',
+ '-€',
+ ],
+ });
+
+ // -##.###,## € (el-GR)
+ test({
+ validator: 'isCurrency',
+ args: [
+ {
+ symbol: '€',
+ thousands_separator: '.',
+ symbol_after_digits: true,
+ decimal_separator: ',',
+ allow_space_after_digits: true,
+ },
+ ],
+ valid: [
+ '123.456,78',
+ '-123.456,78',
+ '6.954.231 €',
+ '-6.954.231 €',
+ '896.954.231',
+ '-896.954.231',
+ '16.954.231',
+ '-16.954.231',
+ '10,03€',
+ '-10,03€',
+ '10,03',
+ '-10,03',
+ '1,39',
+ ',03',
+ '-,03',
+ '-,03 €',
+ '-,03€',
+ '0,10',
+ '10567,01€',
+ '0,01 €',
+ '1.234.567,89€',
+ '10.123€',
+ '10.123',
+ '10.123€',
+ '10.123 €',
+ '10123 €',
+ '10.123',
+ '10123',
+ ],
+ invalid: [
+ '1,234',
+ '1,1 €',
+ ',0001',
+ ',001 €',
+ '0,001€',
+ '12.34,56',
+ '123456.123.123456',
+ '€123€',
+ '',
+ ' ',
+ '€',
+ ' €',
+ '€ ',
+ ' 123',
+ '- 123',
+ '.123',
+ '-.123€',
+ '-.123 €',
+ '123 ',
+ '-€',
+ '- €',
+ '-',
+ '- ',
+ ],
+ });
+
+ // kr. -##.###,## (da-DK)
+ test({
+ validator: 'isCurrency',
+ args: [
+ {
+ symbol: 'kr.',
+ negative_sign_before_digits: true,
+ thousands_separator: '.',
+ decimal_separator: ',',
+ allow_space_after_symbol: true,
+ },
+ ],
+ valid: [
+ '123.456,78',
+ '-10.123',
+ 'kr. -10.123',
+ 'kr.-10.123',
+ 'kr. 6.954.231',
+ 'kr.10,03',
+ 'kr. -10,03',
+ '10,03',
+ '1,39',
+ ',03',
+ '0,10',
+ 'kr. 10567,01',
+ 'kr. 0,01',
+ 'kr. 1.234.567,89',
+ 'kr. -1.234.567,89',
+ '10.123',
+ 'kr. 10.123',
+ 'kr.10.123',
+ '10123',
+ '10.123',
+ 'kr.-10123',
+ ],
+ invalid: [
+ '1,234',
+ 'kr. -10123',
+ 'kr.,1',
+ ',0001',
+ 'kr. ,001',
+ 'kr.0,001',
+ '12.34,56',
+ '123456.123.123456',
+ '.123',
+ 'kr.-.123',
+ 'kr. -.123',
+ '- 123',
+ '123 ',
+ '',
+ ' ',
+ 'kr.',
+ ' kr.',
+ 'kr. ',
+ 'kr.-',
+ 'kr. -',
+ 'kr. - ',
+ ' - ',
+ '-',
+ '- kr.',
+ '-kr.',
+ ],
+ });
+
+ // kr. ##.###,## with no negatives (da-DK)
+ test({
+ validator: 'isCurrency',
+ args: [
+ {
+ symbol: 'kr.',
+ allow_negatives: false,
+ negative_sign_before_digits: true,
+ thousands_separator: '.',
+ decimal_separator: ',',
+ allow_space_after_symbol: true,
+ },
+ ],
+ valid: [
+ '123.456,78',
+ '10.123',
+ 'kr. 10.123',
+ 'kr.10.123',
+ 'kr. 6.954.231',
+ 'kr.10,03',
+ 'kr. 10,03',
+ '10,03',
+ '1,39',
+ ',03',
+ '0,10',
+ 'kr. 10567,01',
+ 'kr. 0,01',
+ 'kr. 1.234.567,89',
+ 'kr.1.234.567,89',
+ '10.123',
+ 'kr. 10.123',
+ 'kr.10.123',
+ '10123',
+ '10.123',
+ 'kr.10123',
+ ],
+ invalid: [
+ '1,234',
+ '-10.123',
+ 'kr. -10.123',
+ 'kr. -1.234.567,89',
+ 'kr.-10123',
+ 'kr. -10123',
+ 'kr.-10.123',
+ 'kr. -10,03',
+ 'kr.,1',
+ ',0001',
+ 'kr. ,001',
+ 'kr.0,001',
+ '12.34,56',
+ '123456.123.123456',
+ '.123',
+ 'kr.-.123',
+ 'kr. -.123',
+ '- 123',
+ '123 ',
+ '',
+ ' ',
+ 'kr.',
+ ' kr.',
+ 'kr. ',
+ 'kr.-',
+ 'kr. -',
+ 'kr. - ',
+ ' - ',
+ '-',
+ '- kr.',
+ '-kr.',
+ ],
+ });
+
+ // ($##,###.##) (en-US, en-HK)
+ test({
+ validator: 'isCurrency',
+ args: [
+ {
+ parens_for_negatives: true,
+ },
+ ],
+ valid: [
+ '1,234',
+ '(1,234)',
+ '($6,954,231)',
+ '$10.03',
+ '(10.03)',
+ '($10.03)',
+ '1.39',
+ '.03',
+ '(.03)',
+ '($.03)',
+ '0.10',
+ '$10567.01',
+ '($0.01)',
+ '$1,234,567.89',
+ '$10,123',
+ '(10,123)',
+ '10123',
+ ],
+ invalid: [
+ '1.234',
+ '($1.1)',
+ '-$1.10',
+ '$ 32.50',
+ '500$',
+ '.0001',
+ '$.001',
+ '($0.001)',
+ '12,34.56',
+ '123456,123,123456',
+ '( 123)',
+ ',123',
+ '$-,123',
+ '',
+ ' ',
+ ' ',
+ ' ',
+ '$',
+ '$ ',
+ ' $',
+ ' 123',
+ '(123) ',
+ '.',
+ ',',
+ '00',
+ '$-',
+ '$ - ',
+ '$- ',
+ ' - ',
+ '-',
+ '- $',
+ '-$',
+ '()',
+ '( )',
+ '( -)',
+ '( - )',
+ '( - )',
+ '(-)',
+ '(-$)',
+ ],
+ });
+ // $##,###.## with no negatives (en-US, en-CA, en-AU, en-HK)
+ test({
+ validator: 'isCurrency',
+ args: [
+ { allow_negatives: false },
+ ],
+ valid: [
+ '$10,123.45',
+ '$10123.45',
+ '10,123.45',
+ '10123.45',
+ '10,123',
+ '1,123,456',
+ '1123456',
+ '1.39',
+ '.03',
+ '0.10',
+ '$0.10',
+ '$100,234,567.89',
+ '$10,123',
+ '10,123',
+ ],
+ invalid: [
+ '1.234',
+ '-1.234',
+ '-10123',
+ '-$0.01',
+ '-$.99',
+ '$1.1',
+ '-$1.1',
+ '$ 32.50',
+ '500$',
+ '.0001',
+ '$.001',
+ '$0.001',
+ '12,34.56',
+ '123456,123,123456',
+ '-123456,123,123456',
+ '123,4',
+ ',123',
+ '$-,123',
+ '$',
+ '.',
+ ',',
+ '00',
+ '$-',
+ '$-,.',
+ '-',
+ '-$',
+ '',
+ '- $',
+ '-$10,123.45',
+ ],
+ });
+
+ // R$ ##,###.## (pt_BR)
+ test({
+ validator: 'isCurrency',
+ args: [
+ {
+ symbol: 'R$',
+ require_symbol: true,
+ allow_space_after_symbol: true,
+ symbol_after_digits: false,
+ thousands_separator: '.',
+ decimal_separator: ',',
+ },
+ ],
+ valid: [
+ 'R$ 1.400,00',
+ 'R$ 400,00',
+ ],
+ invalid: [
+ '$ 1.400,00',
+ '$R 1.400,00',
+ ],
+ });
+ });
+
+ it('should validate Ethereum addresses', () => {
+ test({
+ validator: 'isEthereumAddress',
+ valid: [
+ '0x0000000000000000000000000000000000000001',
+ '0x683E07492fBDfDA84457C16546ac3f433BFaa128',
+ '0x88dA6B6a8D3590e88E0FcadD5CEC56A7C9478319',
+ '0x8a718a84ee7B1621E63E680371e0C03C417cCaF6',
+ '0xFCb5AFB808b5679b4911230Aa41FfCD0cd335b42',
+ ],
+ invalid: [
+ '0xGHIJK05pwm37asdf5555QWERZCXV2345AoEuIdHt',
+ '0xFCb5AFB808b5679b4911230Aa41FfCD0cd335b422222',
+ '0xFCb5AFB808b5679b4911230Aa41FfCD0cd33',
+ '0b0110100001100101011011000110110001101111',
+ '683E07492fBDfDA84457C16546ac3f433BFaa128',
+ '1C6o5CDkLxjsVpnLSuqRs1UBFozXLEwYvU',
+ ],
+ });
+ });
+
+ it('should validate Bitcoin addresses', () => {
+ test({
+ validator: 'isBtcAddress',
+ valid: [
+ '1MUz4VMYui5qY1mxUiG8BQ1Luv6tqkvaiL',
+ 'mucFNhKMYoBQYUAEsrFVscQ1YaFQPekBpg',
+ '3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy',
+ '2NFUBBRcTJbYc1D4HSCbJhKZp6YCV4PQFpQ',
+ 'bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq',
+ '14qViLJfdGaP4EeHnDyJbEGQysnCpwk3gd',
+ '35bSzXvRKLpHsHMrzb82f617cV4Srnt7hS',
+ '17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhemt',
+ 'bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4',
+ 'tb1qxhkl607frtvjsy9nlyeg03lf6fsq947pl2pe82',
+ 'bc1p5d7rjq7g6rdk2yhzks9smlaqtedr4dekq08ge8ztwac72sfr9rusxg3297',
+ 'tb1pzpelffrdh9ptpaqnurwx30dlewqv57rcxfeetp86hsssk30p4cws38tr9y',
+ ],
+ invalid: [
+ '3J98t1WpEZ73CNmQviecrnyiWrnqh0WNL0',
+ '3J98t1WpEZ73CNmQviecrnyiWrnqh0WNLo',
+ '3J98t1WpEZ73CNmQviecrnyiWrnqh0WNLI',
+ '3J98t1WpEZ73CNmQviecrnyiWrnqh0WNLl',
+ '4J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy',
+ '0x56F0B8A998425c53c75C4A303D4eF987533c5597',
+ 'pp8skudq3x5hzw8ew7vzsw8tn4k8wxsqsv0lt0mf3g',
+ '17VZNX1SN5NlKa8UQFxwQbFeFc3iqRYhem',
+ 'BC1QW508D6QEJXTDG4Y5R3ZARVAYR0C5XW7KV8F3T4',
+ 'bc1p5d7rjq7g6rdk2yhzks9smlaqtedr4dekq08ge8ztwac72sfr9rusxg3291',
+ 'bc1p5d7rjq7g6rdk2yhzks9smlaqtedr4dekq08ge8ztwac72sfr9rusxg329b',
+ 'bc1p5d7rjq7g6rdk2yhzks9smlaqtedr4dekq08ge8ztwac72sfr9rusxg329i',
+ 'bc1p5d7rjq7g6rdk2yhzks9smlaqtedr4dekq08ge8ztwac72sfr9rusxg329o',
+ 'BC1P5D7RJQ7G6RDK2YHZKS9SMLAQTEDR4DEKQ08GE8ZTWAC72SFR9RUSXG3297',
+ 'TB1PZPELFFRDH9PTPAQNURWX30DLEWQV57RCXFEETP86HSSSK30P4CWS38TR9Y',
+ ],
+ });
+ });
+
+ it('should validate booleans', () => {
+ test({
+ validator: 'isBoolean',
+ valid: [
+ 'true',
+ 'false',
+ '0',
+ '1',
+ ],
+ invalid: [
+ '1.0',
+ '0.0',
+ 'true ',
+ 'False',
+ 'True',
+ 'yes',
+ ],
+ });
+ });
+
+ it('should validate booleans with option loose set to true', () => {
+ test({
+ validator: 'isBoolean',
+ args: [
+ { loose: true },
+ ],
+ valid: [
+ 'true',
+ 'True',
+ 'TRUE',
+ 'false',
+ 'False',
+ 'FALSE',
+ '0',
+ '1',
+ 'yes',
+ 'Yes',
+ 'YES',
+ 'no',
+ 'No',
+ 'NO',
+ ],
+ invalid: [
+ '1.0',
+ '0.0',
+ 'true ',
+ ' false',
+ ],
+ });
+ });
+
+ it('should validate ISO 639-1 language codes', () => {
+ test({
+ validator: 'isISO6391',
+ valid: ['ay', 'az', 'ba', 'be', 'bg'],
+ invalid: ['aj', 'al', 'pe', 'pf', 'abc', '123', ''],
+ });
+ });
+
+ const validISO8601 = [
+ '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',
+ '2009-10-10',
+ '2020-366',
+ '2000-366',
+ ];
+
+ const invalidISO8601 = [
+ '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',
+ '2010-13-1',
+ 'nonsense2021-01-01T00:00:00Z',
+ '2021-01-01T00:00:00Znonsense',
+ ];
+
+ it('should validate ISO 8601 dates', () => {
+ // from http://www.pelagodesign.com/blog/2009/05/20/iso-8601-date-validation-that-doesnt-suck/
+ test({
+ validator: 'isISO8601',
+ valid: validISO8601,
+ invalid: invalidISO8601,
+ });
+ });
+
+ it('should validate ISO 8601 dates, with strict = true (regression)', () => {
+ test({
+ validator: 'isISO8601',
+ args: [
+ { strict: true },
+ ],
+ valid: validISO8601,
+ invalid: invalidISO8601,
+ });
+ });
+
+ it('should validate ISO 8601 dates, with strict = true', () => {
+ test({
+ validator: 'isISO8601',
+ args: [
+ { strict: true },
+ ],
+ valid: [
+ '2000-02-29',
+ '2009-123',
+ '2009-222',
+ '2020-366',
+ '2400-366',
+ ],
+ invalid: [
+ '2010-02-30',
+ '2009-02-29',
+ '2009-366',
+ '2019-02-31',
+ ],
+ });
+ });
+
+ it('should validate ISO 8601 dates, with strictSeparator = true', () => {
+ test({
+ validator: 'isISO8601',
+ args: [
+ { strictSeparator: true },
+ ],
+ valid: [
+ '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-19T14:39Z',
+ '2009-W21-2',
+ '2009-W21-2T01:22',
+ '2009-139',
+ '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-10-10',
+ '2020-366',
+ '2000-366',
+ ],
+ invalid: [
+ '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',
+ '2010-13-1',
+ '2009-05-19 00:00',
+ // Previously valid cases
+ '2009-05-19 14',
+ '2009-05-19 14:31',
+ '2009-05-19 14:39:22',
+ '2009-05-19 14:39:22-06:00',
+ '2009-05-19 14:39:22+0600',
+ '2009-05-19 14:39:22-01',
+ ],
+ });
+ });
+
+ it('should validate ISO 8601 dates, with strict = true and strictSeparator = true (regression)', () => {
+ test({
+ validator: 'isISO8601',
+ args: [
+ { strict: true, strictSeparator: true },
+ ],
+ valid: [
+ '2000-02-29',
+ '2009-123',
+ '2009-222',
+ '2020-366',
+ '2400-366',
+ ],
+ invalid: [
+ '2010-02-30',
+ '2009-02-29',
+ '2009-366',
+ '2019-02-31',
+ '2009-05-19 14',
+ '2009-05-19 14:31',
+ '2009-05-19 14:39:22',
+ '2009-05-19 14:39:22-06:00',
+ '2009-05-19 14:39:22+0600',
+ '2009-05-19 14:39:22-01',
+ ],
+ });
+ });
+
+ it('should validate ISO 15924 script codes', () => {
+ test({
+ validator: 'isISO15924',
+ valid: [
+ 'Adlm',
+ 'Bass',
+ 'Copt',
+ 'Dsrt',
+ 'Egyd',
+ 'Latn',
+ 'Zzzz',
+ ],
+ invalid: [
+ '',
+ 'arab',
+ 'zzzz',
+ 'Qaby',
+ 'Lati',
+ ],
+ });
+ });
+
+ it('should validate RFC 3339 dates', () => {
+ test({
+ validator: 'isRFC3339',
+ valid: [
+ '2009-05-19 14:39:22-06:00',
+ '2009-05-19 14:39:22+06:00',
+ '2009-05-19 14:39:22Z',
+ '2009-05-19T14:39:22-06:00',
+ '2009-05-19T14:39:22Z',
+ '2010-02-18T16:23:48.3-06:00',
+ '2010-02-18t16:23:33+06:00',
+ '2010-02-18t16:23:33+06:00',
+ '2010-02-18t16:12:23.23334444z',
+ '2010-02-18T16:23:55.2283Z',
+ '2009-05-19 14:39:22.500Z',
+ '2009-05-19 14:39:55Z',
+ '2009-05-31 14:39:55Z',
+ '2009-05-31 14:53:60Z',
+ '2010-02-18t00:23:23.33+06:00',
+ '2010-02-18t00:23:32.33+00:00',
+ '2010-02-18t00:23:32.33+23:00',
+ ],
+ invalid: [
+ '2010-02-18t00:23:32.33+24:00',
+ '2009-05-31 14:60:55Z',
+ '2010-02-18t24:23.33+0600',
+ '2009-05-00 1439,55Z',
+ '2009-13-19 14:39:22-06:00',
+ '2009-05-00 14:39:22+0600',
+ '2009-00-1 14:39:22Z',
+ '2009-05-19T14:39:22',
+ 'nonsense2021-01-01T00:00:00Z',
+ '2021-01-01T00:00:00Znonsense',
+ ],
+ });
+ });
+
+ it('should validate ISO 3166-1 alpha 2 country codes', () => {
+ // from https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2
+ test({
+ validator: 'isISO31661Alpha2',
+ valid: [
+ 'FR',
+ 'fR',
+ 'GB',
+ 'PT',
+ 'CM',
+ 'JP',
+ 'PM',
+ 'ZW',
+ 'MM',
+ 'cc',
+ 'GG',
+ ],
+ invalid: [
+ '',
+ 'FRA',
+ 'AA',
+ 'PI',
+ 'RP',
+ 'WV',
+ 'WL',
+ 'UK',
+ 'ZZ',
+ ],
+ });
+ });
+
+ it('should validate ISO 3166-1 alpha 3 country codes', () => {
+ // from https://en.wikipedia.org/wiki/ISO_3166-1_alpha-3
+ test({
+ validator: 'isISO31661Alpha3',
+ valid: [
+ 'ABW',
+ 'HND',
+ 'KHM',
+ 'RWA',
+ ],
+ invalid: [
+ '',
+ 'FR',
+ 'fR',
+ 'GB',
+ 'PT',
+ 'CM',
+ 'JP',
+ 'PM',
+ 'ZW',
+ ],
+ });
+ });
+
+ it('should validate ISO 3166-1 numeric country codes', () => {
+ // from https://en.wikipedia.org/wiki/ISO_3166-1_numeric
+ test({
+ validator: 'isISO31661Numeric',
+ valid: [
+ '076',
+ '208',
+ '276',
+ '348',
+ '380',
+ '410',
+ '440',
+ '528',
+ '554',
+ '826',
+ ],
+ invalid: [
+ '',
+ 'NL',
+ 'NLD',
+ '002',
+ '197',
+ '249',
+ '569',
+ '810',
+ '900',
+ '999',
+ ],
+ });
+ });
+
+ it('should validate ISO 4217 corrency codes', () => {
+ // from https://en.wikipedia.org/wiki/ISO_4217
+ test({
+ validator: 'isISO4217',
+ valid: [
+ 'AED',
+ 'aed',
+ 'AUD',
+ 'CUP',
+ 'EUR',
+ 'GBP',
+ 'LYD',
+ 'MYR',
+ 'SGD',
+ 'SLE',
+ 'USD',
+ 'VED',
+ 'SLE',
+ ],
+ invalid: [
+ '',
+ '$',
+ 'US',
+ 'us',
+ 'AAA',
+ 'aaa',
+ 'RWA',
+ 'EURO',
+ 'euro',
+ 'HRK',
+ 'CUC',
+ ],
+ });
+ });
+
+ it('should validate whitelisted characters', () => {
+ test({
+ validator: 'isWhitelisted',
+ args: ['abcdefghijklmnopqrstuvwxyz-'],
+ valid: ['foo', 'foobar', 'baz-foo'],
+ invalid: ['foo bar', 'fo.bar', 'türkçe'],
+ });
+ });
+
+ it('should error on non-string input', () => {
+ test({
+ validator: 'isEmpty',
+ error: [undefined, null, [], NaN],
+ });
+ });
+
+ it('should validate dataURI', () => {
+ /* eslint-disable max-len */
+ test({
+ validator: 'isDataURI',
+ valid: [
+ 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD///+l2Z/dAAAAM0lEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4Ug9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC',
+ 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIBAMAAAA2IaO4AAAAFVBMVEXk5OTn5+ft7e319fX29vb5+fn///++GUmVAAAALUlEQVQIHWNICnYLZnALTgpmMGYIFWYIZTA2ZFAzTTFlSDFVMwVyQhmAwsYMAKDaBy0axX/iAAAAAElFTkSuQmCC',
+ 'data:application/media_control+xml;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIBAMAAAA2IaO4AAAAFVBMVEXk5OTn5+ft7e319fX29vb5+fn///++GUmVAAAALUlEQVQIHWNICnYLZnALTgpmMGYIFWYIZTA2ZFAzTTFlSDFVMwVyQhmAwsYMAKDaBy0axX/iAAAAAElFTkSuQmCC',
+ ' data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIBAMAAAA2IaO4AAAAFVBMVEXk5OTn5+ft7e319fX29vb5+fn///++GUmVAAAALUlEQVQIHWNICnYLZnALTgpmMGYIFWYIZTA2ZFAzTTFlSDFVMwVyQhmAwsYMAKDaBy0axX/iAAAAAElFTkSuQmCC ',
+ 'data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22100%22%20height%3D%22100%22%3E%3Crect%20fill%3D%22%2300B1FF%22%20width%3D%22100%22%20height%3D%22100%22%2F%3E%3C%2Fsvg%3E',
+ 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDAiIGhlaWdodD0iMTAwIj48cmVjdCBmaWxsPSIjMDBCMUZGIiB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCIvPjwvc3ZnPg==',
+ ' data:,Hello%2C%20World!',
+ ' data:,Hello World!',
+ ' data:text/plain;base64,SGVsbG8sIFdvcmxkIQ%3D%3D',
+ ' data:text/html,%3Ch1%3EHello%2C%20World!%3C%2Fh1%3E',
+ 'data:,A%20brief%20note',
+ 'data:text/html;charset=US-ASCII,%3Ch1%3EHello!%3C%2Fh1%3E',
+ 'data:application/vnd.openxmlformats-officedocument.wordprocessingml.document;base64,dGVzdC5kb2N4',
+ ],
+ invalid: [
+ 'dataxbase64',
+ 'data:HelloWorld',
+ 'data:,A%20brief%20invalid%20[note',
+ 'file:text/plain;base64,SGVsbG8sIFdvcmxkIQ%3D%3D',
+ 'data:text/html;charset=,%3Ch1%3EHello!%3C%2Fh1%3E',
+ 'data:text/html;charset,%3Ch1%3EHello!%3C%2Fh1%3E', 'data:base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD///+l2Z/dAAAAM0lEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4Ug9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC',
+ '',
+ 'http://wikipedia.org',
+ 'base64',
+ 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD///+l2Z/dAAAAM0lEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4Ug9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC',
+ ],
+ });
+ /* eslint-enable max-len */
+ });
+
+
+ it('should validate magnetURI', () => {
+ /* eslint-disable max-len */
+ test({
+ validator: 'isMagnetURI',
+ valid: [
+ 'magnet:?xt.1=urn:sha1:ABCDEFGHIJKLMNOPQRSTUVWXYZ123456&xt.2=urn:sha1:ABCDEFGHIJKLMNOPQRSTUVWXYZ123456',
+ 'magnet:?xt=urn:btih:ABCDEFGHIJKLMNOPQRSTUVWXYZ12345678901234&dn=helloword2000&tr=udp://helloworld:1337/announce',
+ 'magnet:?xt=urn:btih:ABCDEFGHIJKLMNOPQRSTUVWXYZ12345678901234&dn=foo',
+ 'magnet:?xt=urn:btih:ABCDEFGHIJKLMNOPQRSTUVWXYZ12345678901234&dn=&tr=&nonexisting=hello world',
+ 'magnet:?xt=urn:md5:ABCDEFGHIJKLMNOPQRSTUVWXYZ123456',
+ 'magnet:?xt=urn:tree:tiger:ABCDEFGHIJKLMNOPQRSTUVWXYZ123456',
+ 'magnet:?xt=urn:ed2k:ABCDEFGHIJKLMNOPQRSTUVWXYZ12345678901234',
+ 'magnet:?tr=udp://helloworld:1337/announce&xt=urn:btih:ABCDEFGHIJKLMNOPQRSTUVWXYZ12345678901234',
+ 'magnet:?xt=urn:btmh:1220caf1e1c30e81cb361b9ee167c4aa64228a7fa4fa9f6105232b28ad099f3a302e',
+ ],
+ invalid: [
+ ':?xt=urn:btih:ABCDEFGHIJKLMNOPQRSTUVWXYZ12345678901234',
+ 'xt=urn:btih:ABCDEFGHIJKLMNOPQRSTUVWXYZ12345678901234',
+ 'magneta:?xt=urn:btih:ABCDEFGHIJKLMNOPQRSTUVWXYZ12345678901234',
+ 'magnet:?xt=uarn:btih:ABCDEFGHIJKLMNOPQRSTUVWXYZ12345678901234',
+ 'magnet:?xt=urn:btihz',
+ 'magnet::?xt=urn:btih:UHWY2892JNEJ2GTEYOMDNU67E8ICGICYE92JDUGH',
+ 'magnet:?xt:btih:ABCDEFGHIJKLMNOPQRSTUVWXYZ',
+ 'magnet:?xt:urn:nonexisting:ABCDEFGHIJKLMNOPQRSTUVWXYZ12345678901234',
+ 'magnet:?xt.2=urn:btih:ABCDEFGHIJKLMNOPQRSTUVWXYZ12345678901234',
+ 'magnet:?xt=urn:ed2k:ABCDEFGHIJKLMNOPQRSTUVWXYZ12345678901234567890123456789ABCD',
+ 'magnet:?xt=urn:btmh:1120caf1e1c30e81cb361b9ee167c4aa64228a7fa4fa9f6105232b28ad099f3a302e',
+ 'magnet:?ttxt=urn:btmh:1220caf1e1c30e81cb361b9ee167c4aa64228a7fa4fa9f6105232b28ad099f3a302e',
+ ],
+ });
+ /* eslint-enable max-len */
+ });
+
+
+ it('should validate LatLong', () => {
+ test({
+ validator: 'isLatLong',
+ valid: [
+ '(-17.738223, 85.605469)',
+ '(-12.3456789, +12.3456789)',
+ '(-60.978437, -0.175781)',
+ '(77.719772, -37.529297)',
+ '(7.264394, 165.058594)',
+ '0.955766, -19.863281',
+ '(31.269161,164.355469)',
+ '+12.3456789, -12.3456789',
+ '-15.379543, -137.285156',
+ '(11.770570, -162.949219)',
+ '-55.034319, 113.027344',
+ '58.025555, 36.738281',
+ '55.720923,-28.652344',
+ '-90.00000,-180.00000',
+ '(-71, -146)',
+ '(-71.616864, -146.616864)',
+ '-0.55, +0.22',
+ '90, 180',
+ '+90, -180',
+ '-90,+180',
+ '90,180',
+ '0, 0',
+ ],
+ invalid: [
+ '(020.000000, 010.000000000)',
+ '89.9999999989, 360.0000000',
+ '90.1000000, 180.000000',
+ '+90.000000, -180.00001',
+ '090.0000, 0180.0000',
+ '126, -158',
+ '(-126.400010, -158.400010)',
+ '-95, -96',
+ '-95.738043, -96.738043',
+ '137, -148',
+ '(-137.5942, -148.5942)',
+ '(-120, -203)',
+ '(-119, -196)',
+ '+119.821728, -196.821728',
+ '(-110, -223)',
+ '-110.369532, 223.369532',
+ '(-120.969949, +203.969949)',
+ '-116, -126',
+ '-116.894222, -126.894222',
+ '-112, -160',
+ '-112.96381, -160.96381',
+ '-90., -180.',
+ '+90.1, -180.1',
+ '(-17.738223, 85.605469',
+ '0.955766, -19.863281)',
+ '+,-',
+ '(,)',
+ ',',
+ ' ',
+ ],
+ });
+
+ test({
+ validator: 'isLatLong',
+ args: [{
+ checkDMS: true,
+ }],
+ valid: [
+ '40° 26′ 46″ N, 79° 58′ 56″ W',
+ '40° 26′ 46″ S, 79° 58′ 56″ E',
+ '90° 0′ 0″ S, 180° 0′ 0″ E',
+ '40° 26′ 45.9996″ N, 79° 58′ 55.2″ E',
+ '40° 26′ 46″ n, 79° 58′ 56″ w',
+ '40°26′46″s, 79°58′56″e',
+ '11° 0′ 0.005″ S, 180° 0′ 0″ E',
+ '40°26′45.9996″N, 79°58′55.2″E',
+
+ ],
+ invalid: [
+ '100° 26′ 46″ N, 79° 70′ 56″ W',
+ '40° 89′ 46″ S, 79° 58′ 100″ E',
+ '40° 26.445′ 45″ N, 79° 58′ 55.2″ E',
+ '40° 46″ N, 79° 58′ 56″ W',
+ ],
+ });
+ });
+
+ it('should validate postal code', () => {
+ const fixtures = [
+ {
+ locale: 'AU',
+ valid: [
+ '4000',
+ '2620',
+ '3000',
+ '2017',
+ '0800',
+ ],
+ },
+ {
+ locale: 'BD',
+ valid: [
+ '1000',
+ '1200',
+ '1300',
+ '1400',
+ '1500',
+ '2000',
+ '3000',
+ '4000',
+ '5000',
+ '6000',
+ '7000',
+ '8000',
+ '9000',
+ '9400',
+ '9499',
+ ],
+ invalid: [
+ '0999',
+ '9500',
+ '10000',
+ '12345',
+ '123',
+ '123456',
+ 'abcd',
+ '123a',
+ 'a123',
+ '12 34',
+ '12-34',
+ ],
+ },
+ {
+ locale: 'BY',
+ valid: [
+ '225320',
+ '211120',
+ '247710',
+ '231960',
+ ],
+ invalid: [
+ 'test 225320',
+ '211120 test',
+ '317543',
+ '267946',
+ ],
+ },
+ {
+ locale: 'CA',
+ valid: [
+ 'L4T 0A5',
+ 'G1A-0A2',
+ 'A1A 1A1',
+ 'X0A-0H0',
+ 'V5K 0A1',
+ 'A1C 3S4',
+ 'A1C3S4',
+ 'a1c 3s4',
+ 'V9A 7N2',
+ 'B3K 5X5',
+ 'K8N 5W6',
+ 'K1A 0B1',
+ 'B1Z 0B9',
+ ],
+ invalid: [
+ ' ',
+ 'invalid value',
+ 'a1a1a',
+ 'A1A 1A1',
+ 'K1A 0D1',
+ 'W1A 0B1',
+ 'Z1A 0B1',
+ ],
+ },
+ {
+ locale: 'CO',
+ valid: [
+ '050034',
+ '110221',
+ '441029',
+ '910001',
+ ],
+ invalid: [
+ '11001',
+ '000000',
+ '109999',
+ '329999',
+ ],
+ },
+ {
+ locale: 'ES',
+ valid: [
+ '01001',
+ '52999',
+ '27880',
+ ],
+ invalid: [
+ '123',
+ '1234',
+ '53000',
+ '052999',
+ '0123',
+ 'abcde',
+ ],
+ },
+ {
+ locale: 'JP',
+ valid: [
+ '135-0000',
+ '874-8577',
+ '669-1161',
+ '470-0156',
+ '672-8031',
+ ],
+ },
+ {
+ locale: 'GR',
+ valid: [
+ '022 93',
+ '29934',
+ '90293',
+ '299 42',
+ '94944',
+ ],
+ },
+ {
+ locale: 'GB',
+ valid: [
+ 'TW8 9GS',
+ 'BS98 1TL',
+ 'DE99 3GG',
+ 'DE55 4SW',
+ 'DH98 1BT',
+ 'DH99 1NS',
+ 'GIR0aa',
+ 'SA99',
+ 'W1N 4DJ',
+ 'AA9A 9AA',
+ 'AA99 9AA',
+ 'BS98 1TL',
+ 'DE993GG',
+ ],
+ },
+ {
+ locale: 'FR',
+ valid: [
+ '75008',
+ '44522',
+ '38499',
+ '39940',
+ '01000',
+ ],
+ invalid: [
+ '44 522',
+ '38 499',
+ '96000',
+ '98025',
+ ],
+ },
+ {
+ locale: 'ID',
+ valid: [
+ '10210',
+ '40181',
+ '55161',
+ '60233',
+ ],
+ },
+ {
+ locale: 'IE',
+ valid: [
+ 'A65 TF12',
+ 'A6W U9U9',
+ ],
+ invalid: [
+ '123',
+ '75690HG',
+ 'AW5 TF12',
+ 'AW5 TF12',
+ '756 90HG',
+ 'A65T F12',
+ 'O62 O1O2',
+ ],
+ },
+ {
+ locale: 'IN',
+ valid: [
+ '364240',
+ '360005',
+ ],
+ invalid: [
+ '123',
+ '012345',
+ '011111',
+ '101123',
+ '291123',
+ '351123',
+ '541123',
+ '551123',
+ '651123',
+ '661123',
+ '861123',
+ '871123',
+ '881123',
+ '891123',
+ ],
+ },
+ {
+ locale: 'IL',
+ valid: [
+ '10200',
+ '10292',
+ '10300',
+ '10329',
+ '3885500',
+ '4290500',
+ '4286000',
+ '7080000',
+ ],
+ invalid: [
+ '123',
+ '012345',
+ '011111',
+ '101123',
+ '291123',
+ '351123',
+ '541123',
+ '551123',
+ '651123',
+ '661123',
+ '861123',
+ '871123',
+ '881123',
+ '891123',
+ ],
+ },
+ {
+ locale: 'BG',
+ valid: [
+ '1000',
+ ],
+ },
+ {
+ locale: 'IR',
+ valid: [
+ '4351666456',
+ '5614736867',
+ ],
+ invalid: [
+ '43516 6456',
+ '123443516 6456',
+ '891123',
+ 'test 4351666456',
+ '4351666456 test',
+ 'test 4351666456 test',
+ ],
+ },
+ {
+ locale: 'CZ',
+ valid: [
+ '20134',
+ '392 90',
+ '39919',
+ '938 29',
+ '39949',
+ ],
+ },
+ {
+ locale: 'NL',
+ valid: [
+ '1012 SZ',
+ '3432FE',
+ '1118 BH',
+ '3950IO',
+ '3997 GH',
+ ],
+ invalid: [
+ '1234',
+ '0603 JV',
+ '5194SA',
+ '9164 SD',
+ '1841SS',
+ ],
+ },
+ {
+ locale: 'NP',
+ valid: [
+ '10811',
+ '32600',
+ '56806',
+ '977',
+ ],
+ invalid: [
+ '11977',
+ 'asds',
+ '13 32',
+ '-977',
+ '97765',
+ ],
+ },
+ {
+ locale: 'PL',
+ valid: [
+ '47-260',
+ '12-930',
+ '78-399',
+ '39-490',
+ '38-483',
+ '05-800',
+ '54-060',
+ ],
+ },
+ {
+ locale: 'TW',
+ valid: [
+ '360',
+ '90312',
+ '399',
+ '935',
+ '38842',
+ '546023',
+ ],
+ },
+ {
+ locale: 'LI',
+ valid: [
+ '9485',
+ '9497',
+ '9491',
+ '9489',
+ '9496',
+ ],
+ },
+ {
+ locale: 'PT',
+ valid: [
+ '4829-489',
+ '0294-348',
+ '8156-392',
+ ],
+ },
+ {
+ locale: 'SE',
+ valid: [
+ '12994',
+ '284 39',
+ '39556',
+ '489 39',
+ '499 49',
+ ],
+ },
+ {
+ locale: 'AD',
+ valid: [
+ 'AD100',
+ 'AD200',
+ 'AD300',
+ 'AD400',
+ 'AD500',
+ 'AD600',
+ 'AD700',
+ ],
+ },
+ {
+ locale: 'UA',
+ valid: [
+ '65000',
+ '65080',
+ '01000',
+ '51901',
+ '51909',
+ '49125',
+ ],
+ },
+ {
+ locale: 'BR',
+ valid: [
+ '39100-000',
+ '22040-020',
+ '39400-152',
+ '39100000',
+ '22040020',
+ '39400152',
+ ],
+ invalid: [
+ '79800A12',
+ '13165-00',
+ '38175-abc',
+ '81470-2763',
+ '78908',
+ '13010|111',
+ ],
+ },
+ {
+ locale: 'NZ',
+ valid: [
+ '7843',
+ '3581',
+ '0449',
+ '0984',
+ '4144',
+ ],
+ },
+ {
+ locale: 'PK',
+ valid: [
+ '25000',
+ '44000',
+ '54810',
+ '74200',
+ ],
+ invalid: [
+ '5400',
+ '540000',
+ 'NY540',
+ '540CA',
+ '540-0',
+ ],
+ },
+ {
+ locale: 'MG',
+ valid: [
+ '101',
+ '303',
+ '407',
+ '512',
+ ],
+ },
+ {
+ locale: 'MT',
+ valid: [
+ 'VLT2345',
+ 'VLT 2345',
+ 'ATD1234',
+ 'MSK8723',
+ ],
+ },
+ {
+ locale: 'MY',
+ valid: [
+ '56000',
+ '12000',
+ '79502',
+ ],
+ },
+ {
+ locale: 'PR',
+ valid: [
+ '00979',
+ '00631',
+ '00786',
+ '00987',
+ ],
+ },
+ {
+ locale: 'AZ',
+ valid: [
+ 'AZ0100',
+ 'AZ0121',
+ 'AZ3500',
+ ],
+ invalid: [
+ '',
+ ' AZ0100',
+ 'AZ100',
+ 'AZ34340',
+ 'EN2020',
+ 'AY3030',
+ ],
+ },
+ {
+ locale: 'DO',
+ valid: [
+ '12345',
+ ],
+ invalid: [
+ 'A1234',
+ '123',
+ '123456',
+ ],
+ },
+ {
+ locale: 'HT',
+ valid: [
+ 'HT1234',
+ ],
+ invalid: [
+ 'HT123',
+ 'HT12345',
+ 'AA1234',
+ ],
+ },
+ {
+ locale: 'TH',
+ valid: [
+ '10250',
+ '72170',
+ '12140',
+ ],
+ invalid: [
+ 'T1025',
+ 'T72170',
+ '12140TH',
+ ],
+ },
+ {
+ locale: 'SG',
+ valid: [
+ '308215',
+ '546080',
+ ],
+ },
+ {
+ locale: 'CN',
+ valid: [
+ '150237',
+ '100000',
+ ],
+ invalid: [
+ '141234',
+ '386789',
+ 'ab1234',
+ ],
+ },
+ {
+ locale: 'KR',
+ valid: [
+ '17008',
+ '339012',
+ ],
+ invalid: [
+ '1412347',
+ 'ab1234',
+ ],
+ },
+ {
+ locale: 'LK',
+ valid: [
+ '11500',
+ '22200',
+ '10370',
+ '43000',
+ ],
+ invalid: [
+ '1234',
+ '789389',
+ '982',
+ ],
+ },
+ {
+ locale: 'BA',
+ valid: [
+ '76300',
+ '71000',
+ '75412',
+ '76100',
+ '88202',
+ '88313',
+ ],
+ invalid: [
+ '1234',
+ '789389',
+ '98212',
+ '11000',
+ ],
+ },
+ ];
+
+ let allValid = [];
+
+ // Test fixtures
+ fixtures.forEach((fixture) => {
+ if (fixture.valid) allValid = allValid.concat(fixture.valid);
+ test({
+ validator: 'isPostalCode',
+ valid: fixture.valid,
+ invalid: fixture.invalid,
+ args: [fixture.locale],
+ });
+ });
+
+ // Test generics
+ test({
+ validator: 'isPostalCode',
+ valid: [
+ ...allValid,
+ '1234',
+ '6900',
+ '1292',
+ '9400',
+ '27616',
+ '90210',
+ '10001',
+ '21201',
+ '33142',
+ '060623',
+ '123456',
+ '293940',
+ '002920',
+ ],
+ invalid: [
+ 'asdf',
+ '1',
+ 'ASDFGJKLmZXJtZtesting123',
+ 'Vml2YW11cyBmZXJtZtesting123',
+ '48380480343',
+ '29923-329393-2324',
+ '4294924224',
+ '13',
+ ],
+ args: ['any'],
+ });
+ });
+
+ it('should error on invalid locale', () => {
+ test({
+ validator: 'isPostalCode',
+ args: ['is-NOT'],
+ error: [
+ '293940',
+ '1234',
+ ],
+ });
+ });
+
+ it('should validate MIME types', () => {
+ test({
+ validator: 'isMimeType',
+ valid: [
+ 'application/json',
+ 'application/xhtml+xml',
+ 'audio/mp4',
+ 'image/bmp',
+ 'font/woff2',
+ 'message/http',
+ 'model/vnd.gtw',
+ 'application/media_control+xml',
+ 'multipart/form-data',
+ 'multipart/form-data; boundary=something',
+ 'multipart/form-data; charset=utf-8; boundary=something',
+ 'multipart/form-data; boundary=something; charset=utf-8',
+ 'multipart/form-data; boundary=something; charset="utf-8"',
+ 'multipart/form-data; boundary="something"; charset=utf-8',
+ 'multipart/form-data; boundary="something"; charset="utf-8"',
+ 'text/css',
+ 'text/plain; charset=utf8',
+ 'Text/HTML;Charset="utf-8"',
+ 'text/html;charset=UTF-8',
+ 'Text/html;charset=UTF-8',
+ 'text/html; charset=us-ascii',
+ 'text/html; charset=us-ascii (Plain text)',
+ 'text/html; charset="us-ascii"',
+ 'video/mp4',
+ ],
+ invalid: [
+ '',
+ ' ',
+ '/',
+ 'f/b',
+ 'application',
+ 'application\\json',
+ 'application/json/text',
+ 'application/json; charset=utf-8',
+ 'audio/mp4; charset=utf-8',
+ 'image/bmp; charset=utf-8',
+ 'font/woff2; charset=utf-8',
+ 'message/http; charset=utf-8',
+ 'model/vnd.gtw; charset=utf-8',
+ 'video/mp4; charset=utf-8',
+ ],
+ });
+ });
+
+
+ it('should validate ISO6346 shipping containerID', () => {
+ test({
+ validator: 'isISO6346',
+ valid: [
+ 'HLXU2008419',
+ 'TGHU7599330',
+ 'ECMU4657496',
+ 'MEDU6246078',
+ 'YMLU2809976',
+ 'MRKU0046221',
+ 'EMCU3811879',
+ 'OOLU8643084',
+ 'HJCU1922713',
+ 'QJRZ123456',
+ ],
+ invalid: [
+ 'OOLU1922713',
+ 'HJCU1922413',
+ 'FCUI985619',
+ 'ECMJ4657496',
+ 'TBJA7176445',
+ 'AFFU5962593',
+ ],
+ });
+ });
+ it('should validate ISO6346 shipping containerID', () => {
+ test({
+ validator: 'isFreightContainerID',
+ valid: [
+ 'HLXU2008419',
+ 'TGHU7599330',
+ 'ECMU4657496',
+ 'MEDU6246078',
+ 'YMLU2809976',
+ 'MRKU0046221',
+ 'EMCU3811879',
+ 'OOLU8643084',
+ 'HJCU1922713',
+ 'QJRZ123456',
+ ],
+ invalid: [
+ 'OOLU1922713',
+ 'HJCU1922413',
+ 'FCUI985619',
+ 'ECMJ4657496',
+ 'TBJA7176445',
+ 'AFFU5962593',
+ ],
+ });
+ });
+
+ it('should validate ISO6346 shipping container IDs with checksum digit 10 represented as 0', () => {
+ test({
+ validator: 'isISO6346',
+ valid: [
+ 'APZU3789870',
+ 'TEMU1002030',
+ 'DFSU1704420',
+ 'CMAU2221480',
+ 'SEGU5060260',
+ 'FCIU8939320',
+ 'TRHU3495670',
+ 'MEDU3871410',
+ 'CMAU2184010',
+ 'TCLU2265970',
+ ],
+ invalid: [
+ 'APZU3789871', // Incorrect check digit
+ 'TEMU1002031',
+ 'DFSU1704421',
+ 'CMAU2221481',
+ 'SEGU5060261',
+ ],
+ });
+ });
+ it('should validate ISO6346 shipping container IDs with checksum digit 10 represented as 0', () => {
+ test({
+ validator: 'isFreightContainerID',
+ valid: [
+ 'APZU3789870',
+ 'TEMU1002030',
+ 'DFSU1704420',
+ 'CMAU2221480',
+ 'SEGU5060260',
+ 'FCIU8939320',
+ 'TRHU3495670',
+ 'MEDU3871410',
+ 'CMAU2184010',
+ 'TCLU2265970',
+ ],
+ invalid: [
+ 'APZU3789871', // Incorrect check digit
+ 'TEMU1002031',
+ 'DFSU1704421',
+ 'CMAU2221481',
+ 'SEGU5060261',
+ ],
+ });
+ });
+
+ // EU-UK valid numbers sourced from https://ec.europa.eu/taxation_customs/tin/specs/FS-TIN%20Algorithms-Public.docx or constructed by @tplessas.
+ it('should validate taxID', () => {
+ test({
+ validator: 'isTaxID',
+ args: ['bg-BG'],
+ valid: [
+ '7501010010',
+ '0101010012',
+ '0111010010',
+ '7521010014',
+ '7541010019'],
+ invalid: [
+ '750101001',
+ '75010100101',
+ '75-01010/01 0',
+ '7521320010',
+ '7501010019'],
+ });
+ test({
+ validator: 'isTaxID',
+ args: ['cs-CZ'],
+ valid: [
+ '530121999',
+ '530121/999',
+ '530121/9990',
+ '5301219990',
+ '1602295134',
+ '5451219994',
+ '0424175466',
+ '0532175468',
+ '7159079940'],
+ invalid: [
+ '53-0121 999',
+ '530121000',
+ '960121999',
+ '0124175466',
+ '0472301754',
+ '1975116400',
+ '7159079945'],
+ });
+ test({
+ validator: 'isTaxID',
+ args: ['de-AT'],
+ valid: [
+ '931736581',
+ '93-173/6581',
+ '93--173/6581'],
+ invalid: [
+ '999999999',
+ '93 173 6581',
+ '93-173/65811',
+ '93-173/658'],
+ });
+ test({
+ validator: 'isTaxID',
+ args: ['de-DE'],
+ valid: [
+ '26954371827',
+ '86095742719',
+ '65929970489',
+ '79608434120',
+ '659/299/7048/9'],
+ invalid: [
+ '26954371828',
+ '86095752719',
+ '8609575271',
+ '860957527190',
+ '65299970489',
+ '65999970489',
+ '6592997048-9'],
+ });
+ test({
+ validator: 'isTaxID',
+ args: ['dk-DK'],
+ valid: [
+ '010111-1113',
+ '0101110117',
+ '2110084008',
+ '2110489008',
+ '2110595002',
+ '2110197007',
+ '0101110117',
+ '0101110230'],
+ invalid: [
+ '010111/1113',
+ '010111111',
+ '01011111133',
+ '2110485008',
+ '2902034000',
+ '0101110630'],
+ });
+ test({
+ validator: 'isTaxID',
+ args: ['el-CY'],
+ valid: [
+ '00123123T',
+ '99652156X'],
+ invalid: [
+ '99652156A',
+ '00124123T',
+ '00123123',
+ '001123123T',
+ '00 12-3123/T'],
+ });
+ test({
+ validator: 'isTaxID',
+ args: ['el-GR'],
+ valid: [
+ '758426713',
+ '032792320',
+ '054100004'],
+ invalid: [
+ '054100005',
+ '05410000',
+ '0541000055',
+ '05 4100005',
+ '05-410/0005',
+ '658426713',
+ '558426713'],
+ });
+ test({
+ validator: 'isTaxID',
+ args: ['en-CA'],
+ valid: [
+ '000000000',
+ '521719666',
+ '469317481',
+ '120217450',
+ '480534858',
+ '325268597',
+ '336475660',
+ '744797853',
+ '130692544',
+ '046454286',
+ ],
+ invalid: [
+ ' ',
+ 'any value',
+ '012345678',
+ '111111111',
+ '999999999',
+ '657449110',
+ '74 47 978 53',
+ '744 797 853',
+ '744-797-853',
+ '981062432',
+ '267500713',
+ '2675o0713',
+ '70597312',
+ '7058973122',
+ '069437151',
+ '046454281',
+ '146452286',
+ '30x92544',
+ '30692544',
+ ],
+ });
+ test({
+ validator: 'isTaxID',
+ args: ['en-GB'],
+ valid: [
+ '1234567890',
+ 'AA123456A',
+ 'AA123456 '],
+ invalid: [
+ 'GB123456A',
+ '123456789',
+ '12345678901',
+ 'NK123456A',
+ 'TN123456A',
+ 'ZZ123456A',
+ 'GB123456Z',
+ 'DM123456A',
+ 'AO123456A',
+ 'GB-123456A',
+ 'GB 123456 A',
+ 'GB123456 '],
+ });
+ test({
+ validator: 'isTaxID',
+ args: ['en-IE'],
+ valid: [
+ '1234567T',
+ '1234567TW',
+ '1234577W',
+ '1234577WW',
+ '1234577IA'],
+ invalid: [
+ '1234567',
+ '1234577WWW',
+ '1234577A',
+ '1234577JA'],
+ });
+ test({
+ validator: 'isTaxID',
+ args: ['en-US'],
+ valid: [
+ '01-1234567',
+ '01 1234567',
+ '011234567',
+ '10-1234567',
+ '02-1234567',
+ '67-1234567',
+ '15-1234567',
+ '31-1234567',
+ '99-1234567'],
+ invalid: [
+ '0-11234567',
+ '01#1234567',
+ '01 1234567',
+ '01 1234 567',
+ '07-1234567',
+ '28-1234567',
+ '96-1234567'],
+ });
+ test({
+ validator: 'isTaxID',
+ args: ['es-AR'],
+ valid: [
+ '20271633638',
+ '23274986069',
+ '27333234519',
+ '30678561165',
+ '33693450239',
+ '30534868460',
+ '23111111129',
+ '34557619099'],
+ invalid: [
+ '20-27163363-8',
+ '20.27163363.8',
+ '33693450231',
+ '69345023',
+ '693450233123123',
+ '3369ew50231',
+ '34557619095'],
+ });
+ test({
+ validator: 'isTaxID',
+ args: ['es-ES'],
+ valid: [
+ '00054237A',
+ '54237A',
+ 'X1234567L',
+ 'Z1234567R',
+ 'M2812345C',
+ 'Y2812345B'],
+ invalid: [
+ 'M2812345CR',
+ 'A2812345C',
+ '0/005 423-7A',
+ '00054237U'],
+ });
+ test({
+ validator: 'isTaxID',
+ args: ['et-EE'],
+ valid: [
+ '10001010080',
+ '46304280206',
+ '37102250382',
+ '32708101201'],
+ invalid: [
+ '46304280205',
+ '61002293333',
+ '4-6304 28/0206',
+ '4630428020',
+ '463042802066'],
+ });
+ test({
+ validator: 'isTaxID',
+ args: ['fi-FI'],
+ valid: [
+ '131052-308T',
+ '131002+308W',
+ '131019A3089'],
+ invalid: [
+ '131052308T',
+ '131052-308TT',
+ '131052S308T',
+ '13 1052-308/T',
+ '290219A1111'],
+ });
+ test({
+ validator: 'isTaxID',
+ args: ['fr-BE'],
+ valid: [
+ '00012511119'],
+ });
+ test({
+ validator: 'isTaxID',
+ args: ['fr-FR'],
+ valid: [
+ '30 23 217 600 053',
+ '3023217600053'],
+ invalid: [
+ '30 2 3 217 600 053',
+ '3 023217-600/053',
+ '3023217600052',
+ '3023217500053',
+ '30232176000534',
+ '302321760005'],
+ });
+ test({
+ validator: 'isTaxID',
+ args: ['nl-BE'],
+ valid: [
+ '00012511148',
+ '00/0125-11148',
+ '00000011115'],
+ invalid: [
+ '00 01 2511148',
+ '01022911148',
+ '00013211148',
+ '0001251114',
+ '000125111480',
+ '00012511149'],
+ });
+ test({
+ validator: 'isTaxID',
+ args: ['fr-LU'],
+ valid: [
+ '1893120105732'],
+ invalid: [
+ '189312010573',
+ '18931201057322',
+ '1893 12-01057/32',
+ '1893120105742',
+ '1893120105733'],
+ });
+ test({
+ validator: 'isTaxID',
+ args: ['lb-LU'],
+ invalid: [
+ '2016023005732'],
+ });
+ test({
+ validator: 'isTaxID',
+ args: ['hr-HR'],
+ valid: [
+ '94577403194'],
+ invalid: [
+ '94 57-7403/194',
+ '9457740319',
+ '945774031945',
+ '94577403197',
+ '94587403194'],
+ });
+ test({
+ validator: 'isTaxID',
+ args: ['hu-HU'],
+ valid: [
+ '8071592153'],
+ invalid: [
+ '80 71-592/153',
+ '80715921534',
+ '807159215',
+ '8071592152',
+ '8071582153'],
+ });
+ test({
+ validator: 'isTaxID',
+ args: ['lt-LT'],
+ valid: [
+ '33309240064'],
+ });
+ test({
+ validator: 'isTaxID',
+ args: ['it-IT'],
+ valid: [
+ 'DMLPRY77D15H501F',
+ 'AXXFAXTTD41H501D'],
+ invalid: [
+ 'DML PRY/77D15H501-F',
+ 'DMLPRY77D15H501',
+ 'DMLPRY77D15H501FF',
+ 'AAPPRY77D15H501F',
+ 'DMLAXA77D15H501F',
+ 'AXXFAX90A01Z001F',
+ 'DMLPRY77B29H501F',
+ 'AXXFAX3TD41H501E'],
+ });
+ test({
+ validator: 'isTaxID',
+ args: ['lv-LV'],
+ valid: [
+ '01011012344',
+ '32579461005',
+ '01019902341',
+ '325794-61005'],
+ invalid: [
+ '010110123444',
+ '0101101234',
+ '01001612345',
+ '290217-22343'],
+ });
+ test({
+ validator: 'isTaxID',
+ args: ['mt-MT'],
+ valid: [
+ '1234567A',
+ '882345608',
+ '34581M',
+ '199Z'],
+ invalid: [
+ '812345608',
+ '88234560',
+ '8823456088',
+ '11234567A',
+ '12/34-567 A',
+ '88 23-456/08',
+ '1234560A',
+ '0000000M',
+ '3200100G'],
+ });
+ test({
+ validator: 'isTaxID',
+ args: ['nl-NL'],
+ valid: [
+ '174559434'],
+ invalid: [
+ '17455943',
+ '1745594344',
+ '17 455-94/34'],
+ });
+ test({
+ validator: 'isTaxID',
+ args: ['pl-PL'],
+ valid: [
+ '2234567895',
+ '02070803628',
+ '02870803622',
+ '02670803626',
+ '01510813623'],
+ invalid: [
+ '020708036285',
+ '223456789',
+ '22 345-678/95',
+ '02 070-8036/28',
+ '2234567855',
+ '02223013623'],
+ });
+ test({
+ validator: 'isTaxID',
+ args: ['pt-BR'],
+ valid: [
+ '35161990910',
+ '74407265027',
+ '05423994000172',
+ '11867044000130'],
+ invalid: [
+ 'ABCDEFGH',
+ '170.691.440-72',
+ '11494282142',
+ '74405265037',
+ '11111111111',
+ '48469799384',
+ '94.592.973/0001-82',
+ '28592361000192',
+ '11111111111111',
+ '111111111111112',
+ '61938188550993',
+ '82168365502729',
+ ],
+ });
+ test({
+ validator: 'isTaxID',
+ args: ['pt-PT'],
+ valid: [
+ '299999998',
+ '299992020'],
+ invalid: [
+ '2999999988',
+ '29999999',
+ '29 999-999/8'],
+ });
+ test({
+ validator: 'isTaxID',
+ args: ['ro-RO'],
+ valid: [
+ '8001011234563',
+ '9000123456789',
+ '1001011234560',
+ '3001011234564',
+ '5001011234568'],
+ invalid: [
+ '5001011234569',
+ '500 1011-234/568',
+ '500101123456',
+ '50010112345688',
+ '5001011504568',
+ '8000230234563',
+ '6000230234563'],
+ });
+ test({
+ validator: 'isTaxID',
+ args: ['sk-SK'],
+ valid: [
+ '530121999',
+ '536221/999',
+ '031121999',
+ '520229999',
+ '1234567890'],
+ invalid: [
+ '53012199999',
+ '990101999',
+ '530121000',
+ '53012199',
+ '53-0121 999',
+ '535229999'],
+ });
+ test({
+ validator: 'isTaxID',
+ args: ['sl-SI'],
+ valid: [
+ '15012557',
+ '15012590'],
+ invalid: [
+ '150125577',
+ '1501255',
+ '15 01-255/7'],
+ });
+ test({
+ validator: 'isTaxID',
+ args: ['sv-SE'],
+ valid: [
+ '640823-3234',
+ '640883-3231',
+ '6408833231',
+ '19640823-3233',
+ '196408233233',
+ '19640883-3230',
+ '200228+5266',
+ '20180101-5581'],
+ invalid: [
+ '640823+3234',
+ '160230-3231',
+ '160260-3231',
+ '160260-323',
+ '160260323',
+ '640823+323',
+ '640823323',
+ '640823+32344',
+ '64082332344',
+ '19640823-32333',
+ '1964082332333'],
+ });
+ test({
+ validator: 'isTaxID',
+ args: ['uk-UA'],
+ valid: [
+ '3006321856',
+ '3003102490',
+ '2164212906'],
+ invalid: [
+ '2565975632',
+ '256597563287',
+ 'КС00123456',
+ '2896235845'],
+ });
+ test({
+ validator: 'isTaxID',
+ valid: [
+ '01-1234567'],
+ });
+ test({
+ validator: 'isTaxID',
+ args: ['is-NOT'],
+ error: [
+ '01-1234567',
+ '01 1234567',
+ '011234567',
+ '0-11234567',
+ '01#1234567',
+ '01 1234567',
+ '01 1234 567',
+ '07-1234567',
+ '28-1234567',
+ '96-1234567',
+ ],
+ });
+ });
+
+
+ it('should validate slug', () => {
+ test({
+ validator: 'isSlug',
+ valid: [
+ 'foo',
+ 'foo-bar',
+ 'foo_bar',
+ 'foo-bar-foo',
+ 'foo-bar_foo',
+ 'foo-bar_foo*75-b4r-**_foo',
+ 'foo-bar_foo*75-b4r-**_foo-&&',
+ ],
+ invalid: [
+ 'not-----------slug',
+ '@#_$@',
+ '-not-slug',
+ 'not-slug-',
+ '_not-slug',
+ 'not-slug_',
+ 'not slug',
+ ],
+ });
+ });
+
+ it('should validate strong passwords', () => {
+ test({
+ validator: 'isStrongPassword',
+ args: [{
+ minLength: 8,
+ minLowercase: 1,
+ minUppercase: 1,
+ minNumbers: 1,
+ minSymbols: 1,
+ }],
+ valid: [
+ '%2%k{7BsL"M%Kd6e',
+ 'EXAMPLE of very long_password123!',
+ 'mxH_+2vs&54_+H3P',
+ '+&DxJ=X7-4L8jRCD',
+ 'etV*p%Nr6w&H%FeF',
+ '£3.ndSau_7',
+ 'VaLIDWith\\Symb0l',
+ ],
+ invalid: [
+ '',
+ 'password',
+ 'hunter2',
+ 'hello world',
+ 'passw0rd',
+ 'password!',
+ 'PASSWORD!',
+ ],
+ });
+ });
+
+ it('should validate date', () => {
+ test({
+ validator: 'isDate',
+ valid: [
+ new Date(),
+ new Date([2014, 2, 15]),
+ new Date('2014-03-15'),
+ '2020/02/29',
+ '2020-02-19',
+ ],
+ invalid: [
+ '',
+ '15072002',
+ null,
+ undefined,
+ { year: 2002, month: 7, day: 15 },
+ 42,
+ { toString() { return '[object Date]'; } }, // faking
+ '2020-02-30', // invalid date
+ '2019-02-29', // non-leap year
+ '2020-04-31', // invalid date
+ '2020/03-15', // mixed delimiter
+ '-2020-04-19',
+ '-2023/05/24',
+ 'abc-2023/05/24',
+ '2024',
+ '2024-',
+ '2024-05',
+ '2024-05-',
+ '2024-05-01-',
+ '2024-05-01-abc',
+ '2024/',
+ '2024/05',
+ '2024/05/',
+ '2024/05/01/',
+ '2024/05/01/abc',
+ ],
+ });
+ test({
+ validator: 'isDate',
+ args: ['DD/MM/YYYY'], // old format for backward compatibility
+ valid: [
+ '15-07-2002',
+ '15/07/2002',
+ ],
+ invalid: [
+ '15/7/2002',
+ '15-7-2002',
+ '15/7/02',
+ '15-7-02',
+ '15-07/2002',
+ '2024',
+ '2024-',
+ '2024-05',
+ '2024-05-',
+ '2024-05-01-',
+ '2024-05-01-abc',
+ '2024/',
+ '2024/05',
+ '2024/05/',
+ '2024/05/01/',
+ '2024/05/01/abc',
+ '15/',
+ '15/07',
+ '15/07/',
+ '15/07/2024/',
+ ],
+ });
+ test({
+ validator: 'isDate',
+ args: [{ format: 'DD/MM/YYYY' }],
+ valid: [
+ '15-07-2002',
+ '15/07/2002',
+ ],
+ invalid: [
+ '15/7/2002',
+ '15-7-2002',
+ '15/7/02',
+ '15-7-02',
+ '15-07/2002',
+ '2024',
+ '2024-',
+ '2024-05',
+ '2024-05-',
+ '2024-05-01-',
+ '2024-05-01-abc',
+ '2024/',
+ '2024/05',
+ '2024/05/',
+ '2024/05/01/',
+ '2024/05/01/abc',
+ '15/',
+ '15/07',
+ '15/07/',
+ '15/07/2024/',
+ ],
+ });
+ test({
+ validator: 'isDate',
+ args: [{ format: 'DD/MM/YY' }],
+ valid: [
+ '15-07-02',
+ '15/07/02',
+ ],
+ invalid: [
+ '15/7/2002',
+ '15-7-2002',
+ '15/07-02',
+ '30/04/--',
+ '2024',
+ '2024-',
+ '2024-05',
+ '2024-05-',
+ '2024-05-01-',
+ '2024-05-01-abc',
+ '2024/',
+ '2024/05',
+ '2024/05/',
+ '2024/05/01/',
+ '2024/05/01/abc',
+ '15/',
+ '15/07',
+ '15/07/',
+ '15/07/2024/',
+ '15/07/24/',
+ ],
+ });
+ test({
+ validator: 'isDate',
+ args: [{ format: 'D/M/YY' }],
+ valid: [
+ '5-7-02',
+ '5/7/02',
+ ],
+ invalid: [
+ '5/07/02',
+ '15/7/02',
+ '15-7-02',
+ '5/7-02',
+ '3/4/aa',
+ '2024/',
+ '2024/05',
+ '2024/05/',
+ '2024/05/01/',
+ '2024/05/01/abc',
+ '15/',
+ '15/07',
+ '15/07/',
+ '15/07/2024/',
+ '15/07/24/',
+ ],
+ });
+ test({
+ validator: 'isDate',
+ args: [{ format: 'DD/MM/YYYY', strictMode: true }],
+ valid: [
+ '15/07/2002',
+ ],
+ invalid: [
+ '15-07-2002',
+ '15/7/2002',
+ '15-7-2002',
+ '15/7/02',
+ '15-7-02',
+ '15-07/2002',
+ '2024/',
+ '2024/05',
+ '2024/05/',
+ '2024/05/01/',
+ '2024/05/01/abc',
+ '15/',
+ '15/07',
+ '15/07/',
+ '15/07/2024/',
+ '15/07/24/',
+ ],
+ });
+ test({
+ validator: 'isDate',
+ args: [{ strictMode: true }],
+ valid: [
+ '2020/01/15',
+ '2014/02/15',
+ '2014/03/15',
+ '2020/02/29',
+ ],
+ invalid: [
+ '2014-02-15',
+ '2020-02-29',
+ '15-07/2002',
+ new Date(),
+ new Date([2014, 2, 15]),
+ new Date('2014-03-15'),
+ '-2020-04-19',
+ '-2023/05/24',
+ 'abc-2023/05/24',
+ '2024',
+ '2024-',
+ '2024-05',
+ '2024-05-',
+ '2024-05-01-',
+ '2024-05-01-abc',
+ '2024/',
+ '2024/05',
+ '2024/05/',
+ '2024/05/01/',
+ '2024/05/01/abc',
+ ],
+ });
+ test({
+ validator: 'isDate',
+ args: [{ delimiters: ['/', ' '] }],
+ valid: [
+ new Date(),
+ new Date([2014, 2, 15]),
+ new Date('2014-03-15'),
+ '2020/02/29',
+ '2020 02 29',
+ ],
+ invalid: [
+ '2020-02-29',
+ '',
+ '15072002',
+ null,
+ undefined,
+ { year: 2002, month: 7, day: 15 },
+ 42,
+ { toString() { return '[object Date]'; } },
+ '2020/02/30',
+ '2019/02/29',
+ '2020/04/31',
+ '2020/03-15',
+ '-2020-04-19',
+ '-2023/05/24',
+ 'abc-2023/05/24',
+ '2024',
+ '2024-',
+ '2024-05',
+ '2024-05-',
+ '2024-05-01-',
+ '2024-05-01-abc',
+ '2024/',
+ '2024/05',
+ '2024/05/',
+ '2024/05/01/',
+ '2024/05/01/abc',
+ '2024 05 01 abc',
+ ],
+ });
+ test({
+ validator: 'isDate',
+ args: [{ format: 'MM.DD.YYYY', delimiters: ['.'], strictMode: true }],
+ valid: [
+ '01.15.2020',
+ '02.15.2014',
+ '03.15.2014',
+ '02.29.2020',
+ ],
+ invalid: [
+ '2014-02-15',
+ '2020-02-29',
+ '15-07/2002',
+ new Date(),
+ new Date([2014, 2, 15]),
+ new Date('2014-03-15'),
+ '29.02.2020',
+ '02.29.2020.20',
+ '2024-',
+ '2024-05',
+ '2024-05-',
+ '2024-05-01',
+ '-2020-04-19',
+ '-2023/05/24',
+ 'abc-2023/05/24',
+ '04.05.2024.',
+ '04.05.2024.abc',
+ 'abc.04.05.2024',
+ ],
+ });
+ // emulating Pacific time zone offset & time
+ // which could potentially result in UTC conversion issues
+ timezone_mock.register('US/Pacific');
+ test({
+ validator: 'isDate',
+ valid: [
+ new Date(2016, 2, 29),
+ '2017-08-04',
+ ],
+ });
+ timezone_mock.unregister();
+ });
+ it('should validate time', () => {
+ test({
+ validator: 'isTime',
+ valid: [
+ '00:00',
+ '23:59',
+ '9:00',
+ ],
+ invalid: [
+ '',
+ null,
+ undefined,
+ 0,
+ '07:00 PM',
+ '23',
+ '00:60',
+ '00:',
+ '01:0 ',
+ '001:01',
+ ],
+ });
+ test({
+ validator: 'isTime',
+ args: [{ hourFormat: 'hour24', mode: 'withSeconds' }],
+ valid: [
+ '23:59:59',
+ '00:00:00',
+ '9:50:01',
+ ],
+ invalid: [
+ '',
+ null,
+ undefined,
+ 23,
+ '01:00:01 PM',
+ '13:00:',
+ '00',
+ '26',
+ '00;01',
+ '0 :09',
+ '59:59:59',
+ '24:00:00',
+ '00:59:60',
+ '99:99:99',
+ '009:50:01',
+ ],
+ });
+ test({
+ validator: 'isTime',
+ args: [{ hourFormat: 'hour24', mode: 'withOptionalSeconds' }],
+ valid: [
+ '23:59:59',
+ '00:00:00',
+ '9:50:01',
+ '00:00',
+ '23:59',
+ '9:00',
+ ],
+ invalid: [
+ '',
+ null,
+ undefined,
+ 23,
+ '01:00:01 PM',
+ '13:00:',
+ '00',
+ '26',
+ '00;01',
+ '0 :09',
+ '59:59:59',
+ '24:00:00',
+ '00:59:60',
+ '99:99:99',
+ '009:50:01',
+ ],
+ });
+ test({
+ validator: 'isTime',
+ args: [{ hourFormat: 'hour12' }],
+ valid: [
+ '12:59 PM',
+ '12:59 AM',
+ '01:00 PM',
+ '01:00 AM',
+ '7:00 AM',
+ ],
+ invalid: [
+ '',
+ null,
+ undefined,
+ 0,
+ '12:59 MM',
+ '12:59 MA',
+ '12:59 PA',
+ '12:59 A M',
+ '13:00 PM',
+ '23',
+ '00:60',
+ '00:',
+ '9:00',
+ '01:0 ',
+ '001:01',
+ '12:59:00 PM',
+ '12:59:00 A M',
+ '12:59:00 ',
+ ],
+ });
+ test({
+ validator: 'isTime',
+ args: [{ hourFormat: 'hour12', mode: 'withSeconds' }],
+ valid: [
+ '12:59:59 PM',
+ '2:34:45 AM',
+ '7:00:00 AM',
+ ],
+ invalid: [
+ '',
+ null,
+ undefined,
+ 23,
+ '01:00: 1 PM',
+ '13:00:',
+ '13:00:00 PM',
+ '00',
+ '26',
+ '00;01',
+ '0 :09',
+ '59:59:59',
+ '24:00:00',
+ '00:59:60',
+ '99:99:99',
+ '9:50:01',
+ '009:50:01',
+ ],
+ });
+ test({
+ validator: 'isTime',
+ args: [{ hourFormat: 'hour12', mode: 'withOptionalSeconds' }],
+ valid: [
+ '12:59:59 PM',
+ '2:34:45 AM',
+ '7:00:00 AM',
+ '12:59 PM',
+ '12:59 AM',
+ '01:00 PM',
+ '01:00 AM',
+ '7:00 AM',
+ ],
+ invalid: [
+ '',
+ null,
+ undefined,
+ 23,
+ '01:00: 1 PM',
+ '13:00:',
+ '00',
+ '26',
+ '00;01',
+ '0 :09',
+ '59:59:59',
+ '24:00:00',
+ '00:59:60',
+ '99:99:99',
+ '9:50:01',
+ '009:50:01',
+ ],
+ });
+ });
+ it('should be valid license plate', () => {
+ test({
+ validator: 'isLicensePlate',
+ args: ['es-AR'],
+ valid: [
+ 'AB 123 CD',
+ 'AB123CD',
+ 'ABC 123',
+ 'ABC123',
+ ],
+ invalid: [
+ '',
+ 'notalicenseplate',
+ 'AB-123-CD',
+ 'ABC-123',
+ 'AABC 123',
+ 'AB CDE FG',
+ 'ABC DEF',
+ '12 ABC 34',
+ ],
+ });
+ test({
+ validator: 'isLicensePlate',
+ args: ['pt-PT'],
+ valid: [
+ 'AA-12-34',
+ '12-AA-34',
+ '12-34-AA',
+ 'AA-12-AA',
+ 'AA·12·34',
+ '12·AB·34',
+ '12·34·AB',
+ 'AB·34·AB',
+ 'AA 12 34',
+ '12 AA 34',
+ '12 34 AA',
+ 'AB 12 CD',
+ 'AA1234',
+ '12AA34',
+ '1234AA',
+ 'AB12CD',
+ ],
+ invalid: [
+ '',
+ 'notalicenseplate',
+ 'AA-AA-00',
+ '00-AA-AA',
+ 'AA-AA-AA',
+ '00-00-00',
+ 'AA·AA·00',
+ '00·AA·AA',
+ 'AA·AA·AA',
+ '00·00·00',
+ 'AA AA 00',
+ '00 AA AA',
+ 'AA AA AA',
+ '00 00 00',
+ 'A1-B2-C3',
+ '1A-2B-3C',
+ 'ABC-1-EF',
+ 'AB-C1D-EF',
+ 'AB-C1-DEF',
+ ],
+ });
+ test({
+ validator: 'isLicensePlate',
+ args: ['de-LI'],
+ valid: [
+ 'FL 1',
+ 'FL 99999',
+ 'FL 1337',
+ ],
+ invalid: [
+ '',
+ 'FL 999999',
+ 'AB 12345',
+ 'FL -1',
+ ],
+ });
+ test({
+ validator: 'isLicensePlate',
+ args: ['de-DE'],
+ valid: [
+ 'M A 1',
+ 'M A 12',
+ 'M A 123',
+ 'M A 1234',
+ 'M AB 1',
+ 'M AB 12',
+ 'M AB 123',
+ 'M AB 1234',
+ 'FS A 1',
+ 'FS A 12',
+ 'FS A 123',
+ 'FS A 1234',
+ 'FS AB 1',
+ 'FS AB 12',
+ 'FS AB 123',
+ 'FS AB 1234',
+ 'FSAB1234',
+ 'FS-AB-1234',
+ 'FS AB 1234 H',
+ 'FS AB 1234 E',
+ 'FSAB1234E',
+ 'FS-AB-1234-E',
+ 'FS AB-1234-E',
+ 'FSAB1234 E',
+ 'FS AB1234E',
+ 'LRO AB 123',
+ 'LRO-AB-123-E',
+ 'LRO-AB-123E',
+ 'LRO-AB-123 E',
+ 'LRO-AB-123-H',
+ 'LRO-AB-123H',
+ 'LRO-AB-123 H',
+ ],
+ invalid: [
+ 'YY AB 123',
+ 'PAF AB 1234',
+ 'M ABC 123',
+ 'M AB 12345',
+ 'FS AB 1234 A',
+ 'LRO-AB-1234',
+ 'HRO ABC 123',
+ 'HRO ABC 1234',
+ 'LDK-AB-1234-E',
+ 'ÖHR FA 123D',
+ 'MZG-AB-123X',
+ 'OBG-ABD-123',
+ 'PAF-AB2-123',
+ ],
+ });
+ test({
+ validator: 'isLicensePlate',
+ args: ['fi-FI'],
+ valid: [
+ 'ABC-123',
+ 'ABC 123',
+ 'ABC123',
+ 'A100',
+ 'A 100',
+ 'A-100',
+ 'C10001',
+ 'C 10001',
+ 'C-10001',
+ '123-ABC',
+ '123 ABC',
+ '123ABC',
+ '123-A',
+ '123 A',
+ '123A',
+ '199AA',
+ '199 AA',
+ '199-AA',
+ ],
+ invalid: [
+ ' ',
+ 'A-1',
+ 'A1A-100',
+ '1-A-2',
+ 'C1234567',
+ 'A B C 1 2 3',
+ 'abc-123',
+ ],
+ });
+ test({
+ validator: 'isLicensePlate',
+ args: ['sq-AL'],
+ valid: [
+ 'AA 000 AA',
+ 'ZZ 999 ZZ',
+ ],
+ invalid: [
+ '',
+ 'AA 0 A',
+ 'AAA 00 AAA',
+ ],
+ });
+ test({
+ validator: 'isLicensePlate',
+ args: ['cs-CZ'],
+ valid: [
+ 'ALA4011',
+ '4A23000',
+ 'DICTAT0R',
+ 'VETERAN',
+ 'AZKVIZ8',
+ '2A45876',
+ 'DIC-TAT0R',
+ ],
+ invalid: [
+ '',
+ 'invalidlicenseplate',
+ 'LN5758898',
+ 'X-|$|-X',
+ 'AE0F-OP4',
+ 'GO0MER',
+ '2AAAAAAAA',
+ 'FS AB 1234 E',
+ 'GB999 9999 00',
+ ],
+ });
+
+ test({
+ validator: 'isLicensePlate',
+ args: ['pt-BR'],
+ valid: [
+ 'ABC1234',
+ 'ABC 1234',
+ 'ABC-1234',
+ 'ABC1D23',
+ 'ABC1K23',
+ 'ABC1Z23',
+ 'ABC 1D23',
+ 'ABC-1D23',
+ ],
+ invalid: [
+ '',
+ 'AA 0 A',
+ 'AAA 00 AAA',
+ 'ABCD123',
+ 'AB12345',
+ 'AB123DC',
+ ],
+ });
+ test({
+ validator: 'isLicensePlate',
+ args: ['hu-HU'],
+ valid: [
+ 'AAB-001',
+ 'AVC-987',
+ 'KOC-124',
+ 'JCM-871',
+ 'AWQ-777',
+ 'BPO-001',
+ 'BPI-002',
+ 'UCO-342',
+ 'UDO-385',
+ 'XAO-987',
+ 'AAI-789',
+ 'ABI-789',
+ 'ACI-789',
+ 'AAO-789',
+ 'ABO-789',
+ 'ACO-789',
+ 'YAA-123',
+ 'XAA-123',
+ 'WAA-258',
+ 'XZZ-784',
+ 'M123456',
+ 'CK 12-34',
+ 'DT 12-34',
+ 'CD 12-34',
+ 'HC 12-34',
+ 'HB 12-34',
+ 'HK 12-34',
+ 'MA 12-34',
+ 'OT 12-34',
+ 'RR 17-87',
+ 'CD 124-348',
+ 'C-C 2021',
+ 'C-X 2458',
+ 'X-A 7842',
+ 'E-72345',
+ 'Z-07458',
+ 'S ACF 83',
+ 'SP 04-68',
+ ],
+ invalid: [
+ 'AAA-547',
+ 'aab-001',
+ 'AAB 001',
+ 'AB34',
+ '789-LKJ',
+ 'BBO-987',
+ 'BBI-987',
+ 'BWQ-777',
+ 'BQW-987',
+ 'BAI-789',
+ 'BBI-789',
+ 'BCI-789',
+ 'BAO-789',
+ 'BBO-789',
+ 'BCO-789',
+ 'ADI-789',
+ 'ADO-789',
+ 'KOC-1234',
+ 'M1234567',
+ 'W-12345',
+ 'S BCF 83',
+ 'X-D 1234',
+ 'C-D 1234',
+ 'HU 12-34',
+ ],
+ });
+ test({
+ validator: 'isLicensePlate',
+ args: ['any'],
+ valid: [
+ 'FL 1',
+ 'FS AB 123',
+ ],
+ invalid: [
+ '',
+ 'FL 999999',
+ 'FS AB 1234 A',
+ ],
+ });
+ test({
+ validator: 'isLicensePlate',
+ args: ['asdfasdf'],
+ error: [
+ 'FL 1',
+ 'FS AB 123',
+ 'FL 999999',
+ 'FS AB 1234 A',
+ ],
+ });
+ test({
+ validator: 'isLicensePlate',
+ args: ['sv-SE'],
+ valid: [
+ 'ABC 123',
+ 'ABC 12A',
+ 'ABC123',
+ 'ABC12A',
+ 'A WORD',
+ 'WORD',
+ 'ÅSNA',
+ 'EN VARG',
+ 'CERISE',
+ 'AA',
+ 'ABCDEFG',
+ 'ÅÄÖ',
+ 'ÅÄÖ ÅÄÖ',
+ ],
+ invalid: [
+ '',
+ ' ',
+ 'IQV 123',
+ 'IQV123',
+ 'ABI 12Q',
+ 'ÅÄÖ 123',
+ 'ÅÄÖ 12A',
+ 'AB1 A23',
+ 'AB1 12A',
+ 'lower',
+ 'abc 123',
+ 'abc 12A',
+ 'abc 12a',
+ 'AbC 12a',
+ 'WORDLONGERTHANSEVENCHARACTERS',
+ 'A',
+ 'ABC-123',
+ ],
+ });
+ test({
+ validator: 'isLicensePlate',
+ args: ['en-IN'],
+ valid: [
+ 'MH 04 AD 0001',
+ 'HR26DQ0001',
+ 'WB-04-ZU-2001',
+ 'KL 18 X 5800',
+ 'DL 4 CAF 4856',
+ 'KA-41CE-5289',
+ 'GJ 04-AD 5822',
+ ],
+ invalid: ['mh04ad0045', 'invalidlicenseplate', '4578', '', 'GJ054GH4785'],
+ });
+ test({
+ validator: 'isLicensePlate',
+ args: ['en-SG'],
+ valid: [
+ 'SGX 1234 A',
+ 'SGX-1234-A',
+ 'SGB1234Z',
+ ],
+ invalid: [
+ 'sg1234a',
+ 'invalidlicenseplate',
+ '4578',
+ '',
+ 'GJ054GH4785',
+ ],
+ });
+ });
+ it('should validate VAT numbers', () => {
+ test({
+ validator: 'isVAT',
+ args: ['AT'],
+ valid: [
+ 'ATU12345678',
+ 'U12345678',
+ ],
+ invalid: [
+ 'AT 12345678',
+ '12345678',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['BE'],
+ valid: [
+ 'BE1234567890',
+ '1234567890',
+ ],
+ invalid: [
+ 'BE 1234567890',
+ '123456789',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['BG'],
+ valid: [
+ 'BG1234567890',
+ '1234567890',
+ 'BG123456789',
+ '123456789',
+ ],
+ invalid: [
+ 'BG 1234567890',
+ '12345678',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['HR'],
+ valid: [
+ 'HR12345678901',
+ '12345678901',
+ ],
+ invalid: [
+ 'HR 12345678901',
+ '1234567890',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['CY'],
+ valid: [
+ 'CY123456789',
+ '123456789',
+ ],
+ invalid: [
+ 'CY 123456789',
+ '12345678',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['CZ'],
+ valid: [
+ 'CZ1234567890',
+ 'CZ123456789',
+ 'CZ12345678',
+ '1234567890',
+ '123456789',
+ '12345678',
+ ],
+ invalid: [
+ 'CZ 123456789',
+ '1234567',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['DK'],
+ valid: [
+ 'DK12345678',
+ '12345678',
+ ],
+ invalid: [
+ 'DK 12345678',
+ '1234567',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['EE'],
+ valid: [
+ 'EE123456789',
+ '123456789',
+ ],
+ invalid: [
+ 'EE 123456789',
+ '12345678',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['FI'],
+ valid: [
+ 'FI12345678',
+ '12345678',
+ ],
+ invalid: [
+ 'FI 12345678',
+ '1234567',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['FR'],
+ valid: [
+ 'FRAA123456789',
+ 'FR83404833048',
+ 'FR40123456789',
+ 'FRA1123456789',
+ 'FR1A123456789',
+ ],
+ invalid: [
+ 'FR AA123456789',
+ '123456789',
+ 'FRAA123456789A',
+ 'FR123456789',
+ 'FR 83404833048',
+ 'FRaa123456789',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['DE'],
+ valid: [
+ 'DE123456789',
+ '123456789',
+ ],
+ invalid: [
+ 'DE 123456789',
+ '12345678',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['EL'],
+ valid: [
+ 'EL123456789',
+ '123456789',
+ ],
+ invalid: [
+ 'EL 123456789',
+ '12345678',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['HU'],
+ valid: [
+ 'HU12345678',
+ '12345678',
+ ],
+ invalid: [
+ 'HU 12345678',
+ '1234567',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['IE'],
+ valid: [
+ 'IE1234567AW',
+ '1234567AW',
+ ],
+ invalid: [
+ 'IE 1234567',
+ '1234567',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['IT'],
+ valid: [
+ 'IT12345678910',
+ '12345678910',
+ ],
+ invalid: [
+ 'IT12345678 910',
+ 'IT 123456789101',
+ 'IT123456789101',
+ 'GB12345678910',
+ 'IT123456789',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['LV'],
+ valid: [
+ 'LV12345678901',
+ '12345678901',
+ ],
+ invalid: [
+ 'LV 12345678901',
+ '1234567890',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['LT'],
+ valid: [
+ 'LT123456789012',
+ '123456789012',
+ 'LT12345678901',
+ '12345678901',
+ 'LT1234567890',
+ '1234567890',
+ 'LT123456789',
+ '123456789',
+ ],
+ invalid: [
+ 'LT 123456789012',
+ '12345678',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['LU'],
+ valid: [
+ 'LU12345678',
+ '12345678',
+ ],
+ invalid: [
+ 'LU 12345678',
+ '1234567',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['MT'],
+ valid: [
+ 'MT12345678',
+ '12345678',
+ ],
+ invalid: [
+ 'MT 12345678',
+ '1234567',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['NL'],
+ valid: [
+ 'NL123456789B10',
+ '123456789B10',
+ ],
+ invalid: [
+ 'NL12345678 910',
+ 'NL 123456789101',
+ 'NL123456789B1',
+ 'GB12345678910',
+ 'NL123456789',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['PL'],
+ valid: [
+ 'PL1234567890',
+ '1234567890',
+ 'PL123-456-78-90',
+ '123-456-78-90',
+ 'PL123-45-67-890',
+ '123-45-67-890',
+ ],
+ invalid: [
+ 'PL 1234567890',
+ '123456789',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['PT'],
+ valid: [
+ 'PT123456789',
+ '123456789',
+ ],
+ invalid: [
+ 'PT 123456789',
+ '000000001',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['RO'],
+ valid: [
+ 'RO1234567890',
+ '1234567890',
+ 'RO12',
+ '12',
+ ],
+ invalid: [
+ 'RO 12',
+ '1',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['SK'],
+ valid: [
+ 'SK1234567890',
+ '1234567890',
+ ],
+ invalid: [
+ 'SK 1234567890',
+ '123456789',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['SI'],
+ valid: [
+ 'SI12345678',
+ '12345678',
+ ],
+ invalid: [
+ 'SI 12345678',
+ '1234567',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['ES'],
+ valid: [
+ 'ESA1234567A',
+ 'A1234567A',
+ ],
+ invalid: [
+ 'ES 1234567A',
+ '123456789',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['SE'],
+ valid: [
+ 'SE123456789012',
+ '123456789012',
+ ],
+ invalid: [
+ 'SE 123456789012',
+ '12345678901',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['AL'],
+ valid: [
+ 'AL123456789A',
+ '123456789A',
+ ],
+ invalid: [
+ 'AL 123456789A',
+ '123456789',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['MK'],
+ valid: [
+ 'MK1234567890123',
+ '1234567890123',
+ ],
+ invalid: [
+ 'MK 1234567890123',
+ '123456789012',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['AU'],
+ valid: [
+ 'AU53004085616',
+ '53004085616',
+ 'AU65613309809',
+ '65613309809',
+ 'AU34118972998',
+ '34118972998',
+ ],
+ invalid: [
+ 'AU65613309808',
+ '65613309808',
+ 'AU55613309809',
+ '55613309809',
+ 'AU65613319809',
+ '65613319809',
+ 'AU34117972998',
+ '34117972998',
+ 'AU12345678901',
+ '12345678901',
+ 'AU 12345678901',
+ '1234567890',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['BY'],
+ valid: [
+ 'УНП 123456789',
+ '123456789',
+ ],
+ invalid: [
+ 'BY 123456789',
+ '12345678',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['CA'],
+ valid: [
+ 'CA123456789',
+ '123456789',
+ ],
+ invalid: [
+ 'CA 123456789',
+ '12345678',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['IS'],
+ valid: [
+ 'IS123456',
+ '12345',
+ ],
+ invalid: [
+ 'IS 12345',
+ '1234',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['IN'],
+ valid: [
+ 'IN123456789012345',
+ '123456789012345',
+ ],
+ invalid: [
+ 'IN 123456789012345',
+ '12345678901234',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['ID'],
+ valid: [
+ 'ID123456789012345',
+ '123456789012345',
+ 'ID12.345.678.9-012.345',
+ '12.345.678.9-012.345',
+ ],
+ invalid: [
+ 'ID 123456789012345',
+ '12345678901234',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['IL'],
+ valid: [
+ 'IL123456789',
+ '123456789',
+ ],
+ invalid: [
+ 'IL 123456789',
+ '12345678',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['KZ'],
+ valid: [
+ 'KZ123456789012',
+ '123456789012',
+ ],
+ invalid: [
+ 'KZ 123456789012',
+ '12345678',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['NZ'],
+ valid: [
+ 'NZ123456789',
+ '123456789',
+ ],
+ invalid: [
+ 'NZ 123456789',
+ '12345678',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['NG'],
+ valid: [
+ 'NG123456789012',
+ '123456789012',
+ 'NG12345678-9012',
+ '12345678-9012',
+ ],
+ invalid: [
+ 'NG 123456789012',
+ '12345678901',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['NO'],
+ valid: [
+ 'NO123456789MVA',
+ '123456789MVA',
+ ],
+ invalid: [
+ 'NO 123456789MVA',
+ '123456789',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['PH'],
+ valid: [
+ 'PH123456789012',
+ '123456789012',
+ 'PH123 456 789 012',
+ '123 456 789 012',
+ ],
+ invalid: [
+ 'PH 123456789012',
+ '12345678901',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['RU'],
+ valid: [
+ 'RU1234567890',
+ '1234567890',
+ 'RU123456789012',
+ '123456789012',
+ ],
+ invalid: [
+ 'RU 123456789012',
+ '12345678901',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['SM'],
+ valid: [
+ 'SM12345',
+ '12345',
+ ],
+ invalid: [
+ 'SM 12345',
+ '1234',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['SA'],
+ valid: [
+ 'SA123456789012345',
+ '123456789012345',
+ ],
+ invalid: [
+ 'SA 123456789012345',
+ '12345678901234',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['RS'],
+ valid: [
+ 'RS123456789',
+ '123456789',
+ ],
+ invalid: [
+ 'RS 123456789',
+ '12345678',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['CH'],
+ valid: [
+ // strictly valid
+ 'CHE-116.281.710 MWST',
+ 'CHE-116.281.710 IVA',
+ 'CHE-116.281.710 TVA',
+ // loosely valid presentation variants
+ 'CHE 116 281 710 IVA', // all separators are spaces
+ 'CHE-191.398.369MWST', // no space before suffix
+ 'CHE-116281710 MWST', // no number separators
+ 'CHE-116281710MWST', // no number separators and no space before suffix
+ 'CHE105854263MWST', // no separators
+ 'CHE-116.285.524', // no suffix (vat abbreviation)
+ 'CHE116281710', // no suffix and separators
+ '116.281.710 TVA', // no prefix (CHE, ISO-3166-1 Alpha-3)
+ '116281710MWST', // no prefix and separators
+ '100.218.485', // no prefix and suffix
+ '123456788', // no prefix, separators and suffix
+ ],
+ invalid: [
+ 'CH-116.281.710 MWST', // invalid prefix (should be CHE)
+ 'CHE-116.281 MWST', // invalid number of digits (should be 9)
+ 'CHE-123.456.789 MWST', // invalid last digit (should match the calculated check-number 8)
+ 'CHE-123.356.780 MWST', // invalid check-number (there are no swiss UIDs with the calculated check number 10)
+ 'CH-116.281.710 VAT', // invalid suffix (should be MWST, IVA or TVA)
+ 'CHE-116/281/710 IVA', // invalid number separators (should be all dots or all spaces)
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['TR'],
+ valid: [
+ 'TR1234567890',
+ '1234567890',
+ ],
+ invalid: [
+ 'TR 1234567890',
+ '123456789',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['UA'],
+ valid: [
+ 'UA123456789012',
+ '123456789012',
+ ],
+ invalid: [
+ 'UA 123456789012',
+ '12345678901',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['GB'],
+ valid: [
+ 'GB999 9999 00',
+ 'GB999 9999 96',
+ 'GB999999999 999',
+ 'GBGD000',
+ 'GBGD499',
+ 'GBHA500',
+ 'GBHA999',
+ ],
+ invalid: [
+ 'GB999999900',
+ 'GB999999996',
+ 'GB999 9999 97',
+ 'GB999999999999',
+ 'GB999999999 9999',
+ 'GB9999999999 999',
+ 'GBGD 000',
+ 'GBGD 499',
+ 'GBHA 500',
+ 'GBHA 999',
+ 'GBGD500',
+ 'GBGD999',
+ 'GBHA000',
+ 'GBHA499',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['UZ'],
+ valid: [
+ 'UZ123456789',
+ '123456789',
+ ],
+ invalid: [
+ 'UZ 123456789',
+ '12345678',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['AR'],
+ valid: [
+ 'AR12345678901',
+ '12345678901',
+ ],
+ invalid: [
+ 'AR 12345678901',
+ '1234567890',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['BO'],
+ valid: [
+ 'BO1234567',
+ '1234567',
+ ],
+ invalid: [
+ 'BO 1234567',
+ '123456',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['BR'],
+ valid: [
+ 'BR12.345.678/9012-34',
+ '12.345.678/9012-34',
+ 'BR123.456.789-01',
+ '123.456.789-01',
+ ],
+ invalid: [
+ 'BR 12.345.678/9012-34',
+ '12345678901234',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['CL'],
+ valid: [
+ 'CL12345678-9',
+ '12345678-9',
+ ],
+ invalid: [
+ 'CL 12345678-9',
+ '12345678',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['CO'],
+ valid: [
+ 'CO1234567890',
+ '1234567890',
+ ],
+ invalid: [
+ 'CO 1234567890',
+ '123456789',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['CR'],
+ valid: [
+ 'CR123456789012',
+ '123456789012',
+ 'CR123456789',
+ '123456789',
+ ],
+ invalid: [
+ 'CR 123456789',
+ '12345678',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['EC'],
+ valid: [
+ 'EC1234567890123',
+ '1234567890123',
+ ],
+ invalid: [
+ 'EC 1234567890123',
+ '123456789012',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['SV'],
+ valid: [
+ 'SV1234-567890-123-1',
+ '1234-567890-123-1',
+ ],
+ invalid: [
+ 'SV 1234-567890-123-1',
+ '1234567890123',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['GT'],
+ valid: [
+ 'GT1234567-8',
+ '1234567-8',
+ ],
+ invalid: [
+ 'GT 1234567-8',
+ '1234567',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['HN'],
+ valid: [
+ 'HN',
+ ],
+ invalid: [
+ 'HN ',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['MX'],
+ valid: [
+ 'MXABCD123456EFG',
+ 'ABCD123456EFG',
+ 'MXABC123456DEF',
+ 'ABC123456DEF',
+ ],
+ invalid: [
+ 'MX ABC123456EFG',
+ '123456',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['NI'],
+ valid: [
+ 'NI123-456789-0123A',
+ '123-456789-0123A',
+ ],
+ invalid: [
+ 'NI 123-456789-0123A',
+ '1234567890123',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['PA'],
+ valid: [
+ 'PA',
+ ],
+ invalid: [
+ 'PA ',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['PY'],
+ valid: [
+ 'PY12345678-9',
+ '12345678-9',
+ 'PY123456-7',
+ '123456-7',
+ ],
+ invalid: [
+ 'PY 123456-7',
+ '123456',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['PE'],
+ valid: [
+ 'PE12345678901',
+ '12345678901',
+ ],
+ invalid: [
+ 'PE 12345678901',
+ '1234567890',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['DO'],
+ valid: [
+ 'DO12345678901',
+ '12345678901',
+ 'DO123-4567890-1',
+ '123-4567890-1',
+ 'DO123456789',
+ '123456789',
+ 'DO1-23-45678-9',
+ '1-23-45678-9',
+ ],
+ invalid: [
+ 'DO 12345678901',
+ '1234567890',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['UY'],
+ valid: [
+ 'UY123456789012',
+ '123456789012',
+ ],
+ invalid: [
+ 'UY 123456789012',
+ '12345678901',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['VE'],
+ valid: [
+ 'VEJ-123456789',
+ 'J-123456789',
+ 'VEJ-12345678-9',
+ 'J-12345678-9',
+ ],
+ invalid: [
+ 'VE J-123456789',
+ '12345678',
+ ],
+ });
+ test({
+ validator: 'isVAT',
+ args: ['invalidCountryCode'],
+ error: [
+ 'GB999 9999 00',
+ ],
+ });
+ });
+ it('should validate mailto URI', () => {
+ test({
+ validator: 'isMailtoURI',
+ valid: [
+ 'mailto:?subject=something&cc=valid@mail.com',
+ 'mailto:?subject=something&cc=valid@mail.com,another@mail.com,',
+ 'mailto:?subject=something&bcc=valid@mail.com',
+ 'mailto:?subject=something&bcc=valid@mail.com,another@mail.com',
+ 'mailto:?bcc=valid@mail.com,another@mail.com',
+ 'mailto:?cc=valid@mail.com,another@mail.com',
+ 'mailto:?cc=valid@mail.com',
+ 'mailto:?bcc=valid@mail.com',
+ 'mailto:?subject=something&body=something else',
+ 'mailto:?subject=something&body=something else&cc=hello@mail.com,another@mail.com',
+ 'mailto:?subject=something&body=something else&bcc=hello@mail.com,another@mail.com',
+ 'mailto:?subject=something&body=something else&cc=something@mail.com&bcc=hello@mail.com,another@mail.com',
+ 'mailto:hello@mail.com',
+ 'mailto:info@mail.com?',
+ 'mailto:hey@mail.com?subject=something',
+ 'mailto:info@mail.com?subject=something&cc=valid@mail.com',
+ 'mailto:info@mail.com?subject=something&cc=valid@mail.com,another@mail.com,',
+ 'mailto:info@mail.com?subject=something&bcc=valid@mail.com',
+ 'mailto:info@mail.com?subject=something&bcc=valid@mail.com,another@mail.com',
+ 'mailto:info@mail.com?bcc=valid@mail.com,another@mail.com',
+ 'mailto:info@mail.com?cc=valid@mail.com,another@mail.com',
+ 'mailto:info@mail.com?cc=valid@mail.com',
+ 'mailto:info@mail.com?bcc=valid@mail.com&',
+ 'mailto:info@mail.com?subject=something&body=something else',
+ 'mailto:info@mail.com?subject=something&body=something else&cc=hello@mail.com,another@mail.com',
+ 'mailto:info@mail.com?subject=something&body=something else&bcc=hello@mail.com,another@mail.com',
+ 'mailto:info@mail.com?subject=something&body=something else&cc=something@mail.com&bcc=hello@mail.com,another@mail.com',
+ 'mailto:',
+ ],
+ invalid: [
+ '',
+ 'something',
+ 'valid@gmail.com',
+ 'mailto:?subject=okay&subject=444',
+ 'mailto:?subject=something&wrong=888',
+ 'mailto:somename@gmail.com',
+ 'mailto:hello@world.com?cc=somename@gmail.com',
+ 'mailto:hello@world.com?bcc=somename@gmail.com',
+ 'mailto:hello@world.com?bcc=somename@gmail.com&bcc',
+ 'mailto:valid@gmail.com?subject=anything&body=nothing&cc=&bcc=&key=',
+ 'mailto:hello@world.com?cc=somename',
+ 'mailto:somename',
+ 'mailto:info@mail.com?subject=something&body=something else&cc=something@mail.com&bcc=hello@mail.com,another@mail.com&',
+ 'mailto:?subject=something&body=something else&cc=something@mail.com&bcc=hello@mail.com,another@mail.com&',
+ ],
+ });
+ });
+});
diff --git a/test/validators/isAfter.test.js b/test/validators/isAfter.test.js
new file mode 100644
index 000000000..f0daf8a17
--- /dev/null
+++ b/test/validators/isAfter.test.js
@@ -0,0 +1,76 @@
+import test from '../testFunctions';
+
+describe('isAfter', () => {
+ it('should validate dates against a start date', () => {
+ test({
+ validator: 'isAfter',
+ args: [{ comparisonDate: '2011-08-03' }],
+ valid: ['2011-08-04', new Date(2011, 8, 10).toString()],
+ invalid: ['2010-07-02', '2011-08-03', new Date(0).toString(), 'foo'],
+ });
+
+ test({
+ validator: 'isAfter',
+ valid: ['2100-08-04', new Date(Date.now() + 86400000).toString()],
+ invalid: ['2010-07-02', new Date(0).toString()],
+ });
+
+ test({
+ validator: 'isAfter',
+ args: [{ comparisonDate: '2011-08-03' }],
+ valid: ['2015-09-17'],
+ invalid: ['invalid date'],
+ });
+
+ test({
+ validator: 'isAfter',
+ args: [{ comparisonDate: 'invalid date' }],
+ invalid: ['invalid date', '2015-09-17'],
+ });
+ test({
+ validator: 'isAfter',
+ args: [], // will fall back to the current date
+ valid: ['2100-08-04', new Date(Date.now() + 86400000).toString()],
+ });
+ test({
+ validator: 'isAfter',
+ args: [undefined], // will fall back to the current date
+ valid: ['2100-08-04', new Date(Date.now() + 86400000).toString()],
+ });
+ test({
+ validator: 'isAfter',
+ args: [{ comparisonDate: undefined }], // will fall back to the current date
+ valid: ['2100-08-04', new Date(Date.now() + 86400000).toString()],
+ });
+ });
+
+ describe('(legacy syntax)', () => {
+ it('should validate dates against a start date', () => {
+ test({
+ validator: 'isAfter',
+ args: ['2011-08-03'],
+ valid: ['2011-08-04', new Date(2011, 8, 10).toString()],
+ invalid: ['2010-07-02', '2011-08-03', new Date(0).toString(), 'foo'],
+ });
+
+ test({
+ validator: 'isAfter',
+ valid: ['2100-08-04', new Date(Date.now() + 86400000).toString()],
+ invalid: ['2010-07-02', new Date(0).toString()],
+ });
+
+ test({
+ validator: 'isAfter',
+ args: ['2011-08-03'],
+ valid: ['2015-09-17'],
+ invalid: ['invalid date'],
+ });
+
+ test({
+ validator: 'isAfter',
+ args: ['invalid date'],
+ invalid: ['invalid date', '2015-09-17'],
+ });
+ });
+ });
+});
diff --git a/test/validators/isBase64.test.js b/test/validators/isBase64.test.js
new file mode 100644
index 000000000..c0074343a
--- /dev/null
+++ b/test/validators/isBase64.test.js
@@ -0,0 +1,201 @@
+import { format } from 'util';
+import test from '../testFunctions';
+import validator from '../../src';
+
+describe('isBase64', () => {
+ it('should validate base64 strings with default options', () => {
+ test({
+ validator: 'isBase64',
+ valid: [
+ '',
+ 'Zg==',
+ 'Zm8=',
+ 'Zm9v',
+ 'Zm9vYg==',
+ 'Zm9vYmE=',
+ 'Zm9vYmFy',
+ 'TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdC4=',
+ 'Vml2YW11cyBmZXJtZW50dW0gc2VtcGVyIHBvcnRhLg==',
+ 'U3VzcGVuZGlzc2UgbGVjdHVzIGxlbw==',
+ 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuMPNS1Ufof9EW/M98FNw' +
+ 'UAKrwflsqVxaxQjBQnHQmiI7Vac40t8x7pIb8gLGV6wL7sBTJiPovJ0V7y7oc0Ye' +
+ 'rhKh0Rm4skP2z/jHwwZICgGzBvA0rH8xlhUiTvcwDCJ0kc+fh35hNt8srZQM4619' +
+ 'FTgB66Xmp4EtVyhpQV+t02g6NzK72oZI0vnAvqhpkxLeLiMCyrI416wHm5Tkukhx' +
+ 'QmcL2a6hNOyu0ixX/x2kSFXApEnVrJ+/IxGyfyw8kf4N2IZpW5nEP847lpfj0SZZ' +
+ 'Fwrd1mnfnDbYohX2zRptLy2ZUn06Qo9pkG5ntvFEPo9bfZeULtjYzIl6K8gJ2uGZ' +
+ 'HQIDAQAB',
+ ],
+ invalid: [
+ '12345',
+ 'Vml2YW11cyBmZXJtZtesting123',
+ 'Zg=',
+ 'Z===',
+ 'Zm=8',
+ '=m9vYg==',
+ 'Zm9vYmFy====',
+ ],
+ });
+
+ test({
+ validator: 'isBase64',
+ args: [{ urlSafe: true }],
+ valid: [
+ '',
+ 'bGFkaWVzIGFuZCBnZW50bGVtZW4sIHdlIGFyZSBmbG9hdGluZyBpbiBzcGFjZQ',
+ '1234',
+ 'bXVtLW5ldmVyLXByb3Vk',
+ 'PDw_Pz8-Pg',
+ 'VGhpcyBpcyBhbiBlbmNvZGVkIHN0cmluZw',
+ ],
+ invalid: [
+ ' AA',
+ '\tAA',
+ '\rAA',
+ '\nAA',
+ 'This+isa/bad+base64Url==',
+ '0K3RgtC+INC30LDQutC+0LTQuNGA0L7QstCw0L3QvdCw0Y8g0YHRgtGA0L7QutCw',
+ ],
+ error: [
+ null,
+ undefined,
+ {},
+ [],
+ 42,
+ ],
+ });
+
+ for (let i = 0, str = '', encoded; i < 1000; i++) {
+ str += String.fromCharCode(Math.random() * 26 | 97); // eslint-disable-line no-bitwise
+ encoded = Buffer.from(str).toString('base64');
+ if (!validator.isBase64(encoded)) {
+ let msg = format('validator.isBase64() failed with "%s"', encoded);
+ throw new Error(msg);
+ }
+ }
+ });
+
+ it('should validate standard Base64 with padding', () => {
+ test({
+ validator: 'isBase64',
+ args: [{ urlSafe: false, padding: true }],
+ valid: [
+ '',
+ 'TWFu',
+ 'TWE=',
+ 'TQ==',
+ 'SGVsbG8=',
+ 'U29mdHdhcmU=',
+ 'YW55IGNhcm5hbCBwbGVhc3VyZS4=',
+ ],
+ invalid: [
+ 'TWF',
+ 'TWE===',
+ 'SGVsbG8@',
+ 'SGVsbG8===',
+ 'SGVsb G8=',
+ '====',
+ ],
+ });
+ });
+
+ it('should validate standard Base64 without padding', () => {
+ test({
+ validator: 'isBase64',
+ args: [{ urlSafe: false, padding: false }],
+ valid: [
+ '',
+ 'TWFu',
+ 'TWE',
+ 'TQ',
+ 'SGVsbG8',
+ 'U29mdHdhcmU',
+ 'YW55IGNhcm5hbCBwbGVhc3VyZS4',
+ ],
+ invalid: [
+ 'TWE=',
+ 'TQ===',
+ 'SGVsbG8@',
+ 'SGVsbG8===',
+ 'SGVsb G8',
+ '====',
+ ],
+ });
+ });
+
+ it('should validate Base64url with padding', () => {
+ test({
+ validator: 'isBase64',
+ args: [{ urlSafe: true, padding: true }],
+ valid: [
+ '',
+ 'SGVsbG8=',
+ 'U29mdHdhcmU=',
+ 'YW55IGNhcm5hbCBwbGVhc3VyZS4=',
+ 'SGVsbG8-',
+ 'SGVsbG8_',
+ ],
+ invalid: [
+ 'SGVsbG8===',
+ 'SGVsbG8@',
+ 'SGVsb G8=',
+ '====',
+ ],
+ });
+ });
+
+ it('should validate Base64url without padding', () => {
+ test({
+ validator: 'isBase64',
+ args: [{ urlSafe: true, padding: false }],
+ valid: [
+ '',
+ 'SGVsbG8',
+ 'U29mdHdhcmU',
+ 'YW55IGNhcm5hbCBwbGVhc3VyZS4',
+ 'SGVsbG8-',
+ 'SGVsbG8_',
+ ],
+ invalid: [
+ 'SGVsbG8=',
+ 'SGVsbG8===',
+ 'SGVsbG8@',
+ 'SGVsb G8',
+ '====',
+ ],
+ });
+ });
+
+ it('should handle mixed cases correctly', () => {
+ test({
+ validator: 'isBase64',
+ args: [{ urlSafe: false, padding: true }],
+ valid: [
+ '',
+ 'TWFu',
+ 'TWE=',
+ 'TQ==',
+ ],
+ invalid: [
+ 'TWE',
+ 'TQ=',
+ 'TQ===',
+ ],
+ });
+
+ test({
+ validator: 'isBase64',
+ args: [{ urlSafe: true, padding: false }],
+ valid: [
+ '',
+ 'SGVsbG8',
+ 'SGVsbG8-',
+ 'SGVsbG8_',
+ ],
+ invalid: [
+ 'SGVsbG8=',
+ 'SGVsbG8@',
+ 'SGVsb G8',
+ ],
+ });
+ });
+});
diff --git a/test/validators/isBefore.test.js b/test/validators/isBefore.test.js
new file mode 100644
index 000000000..298e5b410
--- /dev/null
+++ b/test/validators/isBefore.test.js
@@ -0,0 +1,119 @@
+import { describe } from 'mocha';
+import test from '../testFunctions';
+
+describe('isBefore', () => {
+ describe('should validate dates a given end date', () => {
+ describe('new syntax', () => {
+ test({
+ validator: 'isBefore',
+ args: [{ comparisonDate: '08/04/2011' }],
+ valid: ['2010-07-02', '2010-08-04', new Date(0).toString()],
+ invalid: ['08/04/2011', new Date(2011, 9, 10).toString()],
+ });
+ test({
+ validator: 'isBefore',
+ args: [{ comparisonDate: new Date(2011, 7, 4).toString() }],
+ valid: ['2010-07-02', '2010-08-04', new Date(0).toString()],
+ invalid: ['08/04/2011', new Date(2011, 9, 10).toString()],
+ });
+ test({
+ validator: 'isBefore',
+ args: [{ comparisonDate: '2011-08-03' }],
+ valid: ['1999-12-31'],
+ invalid: ['invalid date'],
+ });
+ test({
+ validator: 'isBefore',
+ args: [{ comparisonDate: 'invalid date' }],
+ invalid: ['invalid date', '1999-12-31'],
+ });
+ });
+
+ describe('legacy syntax', () => {
+ test({
+ validator: 'isBefore',
+ args: ['08/04/2011'],
+ valid: ['2010-07-02', '2010-08-04', new Date(0).toString()],
+ invalid: ['08/04/2011', new Date(2011, 9, 10).toString()],
+ });
+ test({
+ validator: 'isBefore',
+ args: [new Date(2011, 7, 4).toString()],
+ valid: ['2010-07-02', '2010-08-04', new Date(0).toString()],
+ invalid: ['08/04/2011', new Date(2011, 9, 10).toString()],
+ });
+ test({
+ validator: 'isBefore',
+ args: ['2011-08-03'],
+ valid: ['1999-12-31'],
+ invalid: ['invalid date'],
+ });
+ test({
+ validator: 'isBefore',
+ args: ['invalid date'],
+ invalid: ['invalid date', '1999-12-31'],
+ });
+ });
+ });
+
+ describe('should validate dates a default end date', () => {
+ describe('new syntax', () => {
+ test({
+ validator: 'isBefore',
+ valid: [
+ '2000-08-04',
+ new Date(0).toString(),
+ new Date(Date.now() - 86400000).toString(),
+ ],
+ invalid: ['2100-07-02', new Date(2217, 10, 10).toString()],
+ });
+ test({
+ validator: 'isBefore',
+ args: undefined, // will fall back to the current date
+ valid: ['1999-06-07'],
+ });
+ test({
+ validator: 'isBefore',
+ args: [], // will fall back to the current date
+ valid: ['1999-06-07'],
+ });
+ test({
+ validator: 'isBefore',
+ args: [undefined], // will fall back to the current date
+ valid: ['1999-06-07'],
+ });
+ test({
+ validator: 'isBefore',
+ args: [{ comparisonDate: undefined }], // will fall back to the current date
+ valid: ['1999-06-07'],
+ });
+ });
+
+ describe('legacy syntax', () => {
+ test({
+ validator: 'isBefore',
+ valid: [
+ '2000-08-04',
+ new Date(0).toString(),
+ new Date(Date.now() - 86400000).toString(),
+ ],
+ invalid: ['2100-07-02', new Date(2217, 10, 10).toString()],
+ });
+ test({
+ validator: 'isBefore',
+ args: undefined, // will fall back to the current date
+ valid: ['1999-06-07'],
+ });
+ test({
+ validator: 'isBefore',
+ args: [], // will fall back to the current date
+ valid: ['1999-06-07'],
+ });
+ test({
+ validator: 'isBefore',
+ args: [undefined], // will fall back to the current date
+ valid: ['1999-06-07'],
+ });
+ });
+ });
+});
diff --git a/test/validators/isFQDN.test.js b/test/validators/isFQDN.test.js
new file mode 100644
index 000000000..134bab005
--- /dev/null
+++ b/test/validators/isFQDN.test.js
@@ -0,0 +1,26 @@
+import test from '../testFunctions';
+
+describe('isFQDN', () => {
+ it('should validate domain names.', () => {
+ test({
+ validator: 'isFQDN',
+ args: [],
+ valid: [
+ 'google.com',
+ ],
+ invalid: [
+ 'google.l33t',
+ ],
+ });
+ test({
+ validator: 'isFQDN',
+ args: [{ allow_numeric_tld: true }],
+ valid: [
+ 'google.com',
+ 'google.l33t',
+ ],
+ invalid: [
+ ],
+ });
+ });
+});
diff --git a/test/validators/isIP.test.js b/test/validators/isIP.test.js
new file mode 100644
index 000000000..62ebbd112
--- /dev/null
+++ b/test/validators/isIP.test.js
@@ -0,0 +1,304 @@
+import test from '../testFunctions';
+
+describe('isIP', () => {
+ it('should validate IP addresses', () => {
+ test({
+ validator: 'isIP',
+ valid: [
+ '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:db8:3:4::192.0.2.33',
+ '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',
+ '::',
+ '::8',
+ '::ffff:127.0.0.1',
+ '::ffff:255.255.255.255',
+ '::ffff:0:255.255.255.255',
+ '::2:3:4:5:6:7:8',
+ '::255.255.255.255',
+ '0:0:0:0:0:ffff:127.0.0.1',
+ '1:2:3:4:5:6:7::',
+ '1:2:3:4:5:6::8',
+ '1::7:8',
+ '1:2:3:4:5::7:8',
+ '1:2:3:4:5::8',
+ '1::6:7:8',
+ '1:2:3:4::6:7:8',
+ '1:2:3:4::8',
+ '1::5:6:7:8',
+ '1:2:3::5:6:7:8',
+ '1:2:3::8',
+ '1::4:5:6:7:8',
+ '1:2::4:5:6:7:8',
+ '1:2::8',
+ '1::3:4:5:6:7:8',
+ '1::8',
+ 'fe80::7:8%eth0',
+ 'fe80::7:8%1',
+ '64:ff9b::192.0.2.33',
+ '0:0:0:0:0:0:10.0.0.1',
+ ],
+ invalid: [
+ 'abc',
+ '256.0.0.0',
+ '0.0.0.256',
+ '26.0.0.256',
+ '0200.200.200.200',
+ '200.0200.200.200',
+ '200.200.0200.200',
+ '200.200.200.0200',
+ '::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',
+ 'BC:e4d5:c:e7b9::%40i0nccymtl9cwfKo.5vaeXLSGRMe:EDh2qs5wkhnPws5xQKqafjfAMm6wGFCJ.bVFsZfb',
+ '1dC:0DF8:62D:3AC::%KTatXocjaFVioS0RTNQl4mA.V151o0RSy.JIu-D-D8.d3171ZWsSJ7PK4YjkJCRN0F',
+ ],
+ });
+
+ test({
+ validator: 'isIP',
+ args: [{ version: 'invalid version' }],
+ valid: [],
+ invalid: [
+ '127.0.0.1',
+ '0.0.0.0',
+ '255.255.255.255',
+ '1.2.3.4',
+ ],
+ });
+
+ test({
+ validator: 'isIP',
+ args: [{ version: null }],
+ valid: [
+ '127.0.0.1',
+ '0.0.0.0',
+ '255.255.255.255',
+ '1.2.3.4',
+ ],
+ });
+
+ test({
+ validator: 'isIP',
+ args: [{ version: undefined }],
+ valid: [
+ '127.0.0.1',
+ '0.0.0.0',
+ '255.255.255.255',
+ '1.2.3.4',
+ ],
+ });
+
+ test({
+ validator: 'isIP',
+ args: [{ version: 4 }],
+ valid: [
+ '127.0.0.1',
+ '0.0.0.0',
+ '255.255.255.255',
+ '1.2.3.4',
+ '255.0.0.1',
+ '0.0.1.1',
+ ],
+ invalid: [
+ '::1',
+ '2001:db8:0000:1:1:1:1:1',
+ '::ffff:127.0.0.1',
+ '137.132.10.01',
+ '0.256.0.256',
+ '255.256.255.256',
+ ],
+ });
+
+ test({
+ validator: 'isIP',
+ args: [{ version: 6 }],
+ valid: [
+ '::1',
+ '2001:db8:0000:1:1:1:1:1',
+ '::ffff:127.0.0.1',
+ 'fe80::1234%1',
+ 'ff08::9abc%10',
+ 'ff08::9abc%interface10',
+ 'ff02::5678%pvc1.3',
+ ],
+ invalid: [
+ '127.0.0.1',
+ '0.0.0.0',
+ '255.255.255.255',
+ '1.2.3.4',
+ '::ffff:287.0.0.1',
+ '%',
+ 'fe80::1234%',
+ 'fe80::1234%1%3%4',
+ 'fe80%fe80%',
+ ],
+ });
+
+ test({
+ validator: 'isIP',
+ args: [{ version: 10 }],
+ valid: [],
+ invalid: [
+ '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',
+ ],
+ });
+ });
+
+ describe('legacy syntax', () => {
+ it('should validate IP addresses', () => {
+ test({
+ validator: 'isIP',
+ valid: [
+ '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:db8:3:4::192.0.2.33',
+ '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',
+ '::',
+ '::8',
+ '::ffff:127.0.0.1',
+ '::ffff:255.255.255.255',
+ '::ffff:0:255.255.255.255',
+ '::2:3:4:5:6:7:8',
+ '::255.255.255.255',
+ '0:0:0:0:0:ffff:127.0.0.1',
+ '1:2:3:4:5:6:7::',
+ '1:2:3:4:5:6::8',
+ '1::7:8',
+ '1:2:3:4:5::7:8',
+ '1:2:3:4:5::8',
+ '1::6:7:8',
+ '1:2:3:4::6:7:8',
+ '1:2:3:4::8',
+ '1::5:6:7:8',
+ '1:2:3::5:6:7:8',
+ '1:2:3::8',
+ '1::4:5:6:7:8',
+ '1:2::4:5:6:7:8',
+ '1:2::8',
+ '1::3:4:5:6:7:8',
+ '1::8',
+ 'fe80::7:8%eth0',
+ 'fe80::7:8%1',
+ '64:ff9b::192.0.2.33',
+ '0:0:0:0:0:0:10.0.0.1',
+ ],
+ invalid: [
+ 'abc',
+ '256.0.0.0',
+ '0.0.0.256',
+ '26.0.0.256',
+ '0200.200.200.200',
+ '200.0200.200.200',
+ '200.200.0200.200',
+ '200.200.200.0200',
+ '::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',
+ ],
+ });
+ test({
+ validator: 'isIP',
+ args: [4],
+ valid: [
+ '127.0.0.1',
+ '0.0.0.0',
+ '255.255.255.255',
+ '1.2.3.4',
+ '255.0.0.1',
+ '0.0.1.1',
+ ],
+ invalid: [
+ '::1',
+ '2001:db8:0000:1:1:1:1:1',
+ '::ffff:127.0.0.1',
+ '137.132.10.01',
+ '0.256.0.256',
+ '255.256.255.256',
+ ],
+ });
+ test({
+ validator: 'isIP',
+ args: [6],
+ valid: [
+ '::1',
+ '2001:db8:0000:1:1:1:1:1',
+ '::ffff:127.0.0.1',
+ 'fe80::1234%1',
+ 'ff08::9abc%10',
+ 'ff08::9abc%interface10',
+ 'ff02::5678%pvc1.3',
+ ],
+ invalid: [
+ '127.0.0.1',
+ '0.0.0.0',
+ '255.255.255.255',
+ '1.2.3.4',
+ '::ffff:287.0.0.1',
+ '%',
+ 'fe80::1234%',
+ 'fe80::1234%1%3%4',
+ 'fe80%fe80%',
+ ],
+ });
+ test({
+ validator: 'isIP',
+ args: [10],
+ valid: [],
+ invalid: [
+ '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',
+ ],
+ });
+ });
+ });
+});
diff --git a/test/validators/isISBN.test.js b/test/validators/isISBN.test.js
new file mode 100644
index 000000000..99fb2e014
--- /dev/null
+++ b/test/validators/isISBN.test.js
@@ -0,0 +1,109 @@
+import test from '../testFunctions';
+
+describe('isISBN', () => {
+ it('should validate ISBNs', () => {
+ test({
+ validator: 'isISBN',
+ args: [{ version: 10 }],
+ valid: [
+ '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',
+ ],
+ invalid: [
+ '3423214121', '3-423-21412-1', '3 423 21412 1',
+ '978-3836221191', '9783836221191',
+ '123456789a', 'foo', '',
+ ],
+ });
+ test({
+ validator: 'isISBN',
+ args: [{ version: 13 }],
+ valid: [
+ '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',
+ ],
+ invalid: [
+ '9783836221190', '978-3-8362-2119-0', '978 3 8362 2119 0',
+ '3836221195', '3-8362-2119-5', '3 8362 2119 5',
+ '01234567890ab', 'foo', '',
+ ],
+ });
+ test({
+ validator: 'isISBN',
+ valid: [
+ '340101319X',
+ '9784873113685',
+ ],
+ invalid: [
+ '3423214121',
+ '9783836221190',
+ ],
+ });
+ test({
+ validator: 'isISBN',
+ args: [{ version: 'foo' }],
+ invalid: [
+ '340101319X',
+ '9784873113685',
+ ],
+ });
+ });
+
+ describe('(legacy syntax)', () => {
+ it('should validate ISBNs', () => {
+ test({
+ validator: 'isISBN',
+ args: [10],
+ valid: [
+ '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',
+ ],
+ invalid: [
+ '3423214121', '3-423-21412-1', '3 423 21412 1',
+ '978-3836221191', '9783836221191',
+ '123456789a', 'foo', '',
+ ],
+ });
+ test({
+ validator: 'isISBN',
+ args: [13],
+ valid: [
+ '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',
+ ],
+ invalid: [
+ '9783836221190', '978-3-8362-2119-0', '978 3 8362 2119 0',
+ '3836221195', '3-8362-2119-5', '3 8362 2119 5',
+ '01234567890ab', 'foo', '',
+ ],
+ });
+ test({
+ validator: 'isISBN',
+ valid: [
+ '340101319X',
+ '9784873113685',
+ ],
+ invalid: [
+ '3423214121',
+ '9783836221190',
+ ],
+ });
+ test({
+ validator: 'isISBN',
+ args: ['foo'],
+ invalid: [
+ '340101319X',
+ '9784873113685',
+ ],
+ });
+ });
+ });
+});
diff --git a/test/validators/isLength.test.js b/test/validators/isLength.test.js
new file mode 100644
index 000000000..5c1444002
--- /dev/null
+++ b/test/validators/isLength.test.js
@@ -0,0 +1,144 @@
+import test from '../testFunctions';
+
+describe('isLength', () => {
+ it('should return false for a string with length greater than the max', () => {
+ test({
+ validator: 'isLength',
+ args: [{ max: 3 }],
+ invalid: ['test'],
+ });
+ });
+
+ it('should return true for a string with length equal to the max', () => {
+ test({
+ validator: 'isLength',
+ args: [{ max: 4 }],
+ valid: ['test'],
+ });
+ });
+
+ it('should correctly calculate the length of a string with presentation sequences', () => {
+ test({
+ validator: 'isLength',
+ args: [{ max: 4 }],
+ valid: ['test\uFE0F'],
+ });
+
+ test({
+ validator: 'isLength',
+ args: [{ min: 5, max: 5 }],
+ valid: ['test\uFE0F\uFE0F'],
+ });
+
+ test({
+ validator: 'isLength',
+ args: [{ min: 5, max: 5 }],
+ valid: ['\uFE0Ftest'],
+ });
+
+ test({
+ validator: 'isLength',
+ args: [{ min: 9, max: 9 }],
+ valid: ['test\uFE0F\uFE0F\uFE0F\uFE0F\uFE0F\uFE0F'],
+ });
+ });
+
+ it('should validate strings by length (deprecated api)', () => {
+ test({
+ validator: 'isLength',
+ args: [2],
+ valid: ['abc', 'de', 'abcd'],
+ invalid: ['', 'a'],
+ });
+ test({
+ validator: 'isLength',
+ args: [2, 3],
+ valid: ['abc', 'de'],
+ invalid: ['', 'a', 'abcd'],
+ });
+ test({
+ validator: 'isLength',
+ args: [2, 3],
+ valid: ['干𩸽', '𠮷野家'],
+ invalid: ['', '𠀋', '千竈通り'],
+ });
+ test({
+ validator: 'isLength',
+ args: [0, 0],
+ valid: [''],
+ invalid: ['a', 'ab'],
+ });
+ });
+
+ it('should validate strings by length', () => {
+ test({
+ validator: 'isLength',
+ args: [{ min: 2 }],
+ valid: ['abc', 'de', 'abcd'],
+ invalid: ['', 'a'],
+ });
+ test({
+ validator: 'isLength',
+ args: [{ min: 2, max: 3 }],
+ valid: ['abc', 'de'],
+ invalid: ['', 'a', 'abcd'],
+ });
+ test({
+ validator: 'isLength',
+ args: [{ min: 2, max: 3 }],
+ valid: ['干𩸽', '𠮷野家'],
+ invalid: ['', '𠀋', '千竈通り'],
+ });
+ test({
+ validator: 'isLength',
+ args: [{ max: 3 }],
+ valid: ['abc', 'de', 'a', ''],
+ invalid: ['abcd'],
+ });
+ test({
+ validator: 'isLength',
+ args: [{ max: 6, discreteLengths: 5 }],
+ valid: ['abcd', 'vfd', 'ff', '', 'k'],
+ invalid: ['abcdefgh', 'hfjdksks'],
+ });
+ test({
+ validator: 'isLength',
+ args: [{ min: 2, max: 6, discreteLengths: 5 }],
+ valid: ['bsa', 'vfvd', 'ff'],
+ invalid: ['', ' ', 'hfskdunvc'],
+ });
+ test({
+ validator: 'isLength',
+ args: [{ min: 1, discreteLengths: 2 }],
+ valid: [' ', 'hello', 'bsa'],
+ invalid: [''],
+ });
+ test({
+ validator: 'isLength',
+ args: [{ max: 0 }],
+ valid: [''],
+ invalid: ['a', 'ab'],
+ });
+ test({
+ validator: 'isLength',
+ args: [{ min: 5, max: 10, discreteLengths: [2, 6, 8, 9] }],
+ valid: ['helloguy', 'shopping', 'validator', 'length'],
+ invalid: ['abcde', 'abcdefg'],
+ });
+ test({
+ validator: 'isLength',
+ args: [{ discreteLengths: '9' }],
+ valid: ['a', 'abcd', 'abcdefghijkl'],
+ invalid: [],
+ });
+ test({
+ validator: 'isLength',
+ valid: ['a', '', 'asds'],
+ });
+ test({
+ validator: 'isLength',
+ args: [{ max: 8 }],
+ valid: ['👩🦰👩👩👦👦🏳️🌈', '⏩︎⏩︎⏪︎⏪︎⏭︎⏭︎⏮︎⏮︎'],
+ });
+ });
+});
diff --git a/validator.js b/validator.js
deleted file mode 100644
index 45718aa94..000000000
--- a/validator.js
+++ /dev/null
@@ -1,1477 +0,0 @@
-/*!
- * Copyright (c) 2016 Chris O'Hara
- *
- * 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.
- */
-(function (global, factory) {
- typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
- typeof define === 'function' && define.amd ? define(factory) :
- (global.validator = factory());
-}(this, (function () { 'use strict';
-
-function assertString(input) {
- var isString = typeof input === 'string' || input instanceof String;
-
- if (!isString) {
- throw new TypeError('This library (validator.js) validates strings only');
- }
-}
-
-function toDate(date) {
- assertString(date);
- date = Date.parse(date);
- return !isNaN(date) ? new Date(date) : null;
-}
-
-function toFloat(str) {
- assertString(str);
- return parseFloat(str);
-}
-
-function toInt(str, radix) {
- assertString(str);
- return parseInt(str, radix || 10);
-}
-
-function toBoolean(str, strict) {
- assertString(str);
- if (strict) {
- return str === '1' || str === 'true';
- }
- return str !== '0' && str !== 'false' && str !== '';
-}
-
-function equals(str, comparison) {
- assertString(str);
- return str === comparison;
-}
-
-var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
- return typeof obj;
-} : function (obj) {
- return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
-};
-
-function toString(input) {
- if ((typeof input === 'undefined' ? 'undefined' : _typeof(input)) === 'object' && input !== null) {
- if (typeof input.toString === 'function') {
- input = input.toString();
- } else {
- input = '[object Object]';
- }
- } else if (input === null || typeof input === 'undefined' || isNaN(input) && !input.length) {
- input = '';
- }
- return String(input);
-}
-
-function contains(str, elem) {
- assertString(str);
- return str.indexOf(toString(elem)) >= 0;
-}
-
-function matches(str, pattern, modifiers) {
- assertString(str);
- if (Object.prototype.toString.call(pattern) !== '[object RegExp]') {
- pattern = new RegExp(pattern, modifiers);
- }
- return pattern.test(str);
-}
-
-function merge() {
- var obj = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
- var defaults = arguments[1];
-
- for (var key in defaults) {
- if (typeof obj[key] === 'undefined') {
- obj[key] = defaults[key];
- }
- }
- return obj;
-}
-
-/* eslint-disable prefer-rest-params */
-function isByteLength(str, options) {
- assertString(str);
- var min = void 0;
- var max = void 0;
- if ((typeof options === 'undefined' ? 'undefined' : _typeof(options)) === 'object') {
- min = options.min || 0;
- max = options.max;
- } else {
- // backwards compatibility: isByteLength(str, min [, max])
- min = arguments[1];
- max = arguments[2];
- }
- var len = encodeURI(str).split(/%..|./).length - 1;
- return len >= min && (typeof max === 'undefined' || len <= max);
-}
-
-var default_fqdn_options = {
- require_tld: true,
- allow_underscores: false,
- allow_trailing_dot: false
-};
-
-function isFQDN(str, options) {
- assertString(str);
- options = merge(options, default_fqdn_options);
-
- /* Remove the optional trailing dot before checking validity */
- if (options.allow_trailing_dot && str[str.length - 1] === '.') {
- str = str.substring(0, str.length - 1);
- }
- var parts = str.split('.');
- if (options.require_tld) {
- var tld = parts.pop();
- if (!parts.length || !/^([a-z\u00a1-\uffff]{2,}|xn[a-z0-9-]{2,})$/i.test(tld)) {
- return false;
- }
- // disallow spaces
- if (/[\s\u2002-\u200B\u202F\u205F\u3000\uFEFF\uDB40\uDC20]/.test(tld)) {
- return false;
- }
- }
- for (var part, i = 0; i < parts.length; i++) {
- part = parts[i];
- if (options.allow_underscores) {
- part = part.replace(/_/g, '');
- }
- if (!/^[a-z\u00a1-\uffff0-9-]+$/i.test(part)) {
- return false;
- }
- // disallow full-width chars
- if (/[\uff01-\uff5e]/.test(part)) {
- return false;
- }
- if (part[0] === '-' || part[part.length - 1] === '-') {
- return false;
- }
- }
- return true;
-}
-
-var default_email_options = {
- allow_display_name: false,
- require_display_name: false,
- allow_utf8_local_part: true,
- require_tld: true
-};
-
-/* eslint-disable max-len */
-/* eslint-disable no-control-regex */
-var displayName = /^[a-z\d!#\$%&'\*\+\-\/=\?\^_`{\|}~\.\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+[a-z\d!#\$%&'\*\+\-\/=\?\^_`{\|}~\,\.\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF\s]*<(.+)>$/i;
-var emailUserPart = /^[a-z\d!#\$%&'\*\+\-\/=\?\^_`{\|}~]+$/i;
-var quotedEmailUser = /^([\s\x01-\x08\x0b\x0c\x0e-\x1f\x7f\x21\x23-\x5b\x5d-\x7e]|(\\[\x01-\x09\x0b\x0c\x0d-\x7f]))*$/i;
-var emailUserUtf8Part = /^[a-z\d!#\$%&'\*\+\-\/=\?\^_`{\|}~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+$/i;
-var quotedEmailUserUtf8 = /^([\s\x01-\x08\x0b\x0c\x0e-\x1f\x7f\x21\x23-\x5b\x5d-\x7e\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|(\\[\x01-\x09\x0b\x0c\x0d-\x7f\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))*$/i;
-/* eslint-enable max-len */
-/* eslint-enable no-control-regex */
-
-function isEmail(str, options) {
- assertString(str);
- options = merge(options, default_email_options);
-
- if (options.require_display_name || options.allow_display_name) {
- var display_email = str.match(displayName);
- if (display_email) {
- str = display_email[1];
- } else if (options.require_display_name) {
- return false;
- }
- }
-
- var parts = str.split('@');
- var domain = parts.pop();
- var user = parts.join('@');
-
- var lower_domain = domain.toLowerCase();
- if (lower_domain === 'gmail.com' || lower_domain === 'googlemail.com') {
- user = user.replace(/\./g, '').toLowerCase();
- }
-
- if (!isByteLength(user, { max: 64 }) || !isByteLength(domain, { max: 254 })) {
- return false;
- }
-
- if (!isFQDN(domain, { require_tld: options.require_tld })) {
- return false;
- }
-
- if (user[0] === '"') {
- user = user.slice(1, user.length - 1);
- return options.allow_utf8_local_part ? quotedEmailUserUtf8.test(user) : quotedEmailUser.test(user);
- }
-
- var pattern = options.allow_utf8_local_part ? emailUserUtf8Part : emailUserPart;
-
- var user_parts = user.split('.');
- for (var i = 0; i < user_parts.length; i++) {
- if (!pattern.test(user_parts[i])) {
- return false;
- }
- }
-
- return true;
-}
-
-var ipv4Maybe = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/;
-var ipv6Block = /^[0-9A-F]{1,4}$/i;
-
-function isIP(str) {
- var version = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
-
- assertString(str);
- version = String(version);
- if (!version) {
- return isIP(str, 4) || isIP(str, 6);
- } else if (version === '4') {
- if (!ipv4Maybe.test(str)) {
- return false;
- }
- var parts = str.split('.').sort(function (a, b) {
- return a - b;
- });
- return parts[3] <= 255;
- } else if (version === '6') {
- var blocks = str.split(':');
- var foundOmissionBlock = false; // marker to indicate ::
-
- // At least some OS accept the last 32 bits of an IPv6 address
- // (i.e. 2 of the blocks) in IPv4 notation, and RFC 3493 says
- // that '::ffff:a.b.c.d' is valid for IPv4-mapped IPv6 addresses,
- // and '::a.b.c.d' is deprecated, but also valid.
- var foundIPv4TransitionBlock = isIP(blocks[blocks.length - 1], 4);
- var expectedNumberOfBlocks = foundIPv4TransitionBlock ? 7 : 8;
-
- if (blocks.length > expectedNumberOfBlocks) {
- return false;
- }
- // initial or final ::
- if (str === '::') {
- return true;
- } else if (str.substr(0, 2) === '::') {
- blocks.shift();
- blocks.shift();
- foundOmissionBlock = true;
- } else if (str.substr(str.length - 2) === '::') {
- blocks.pop();
- blocks.pop();
- foundOmissionBlock = true;
- }
-
- for (var i = 0; i < blocks.length; ++i) {
- // test for a :: which can not be at the string start/end
- // since those cases have been handled above
- if (blocks[i] === '' && i > 0 && i < blocks.length - 1) {
- if (foundOmissionBlock) {
- return false; // multiple :: in address
- }
- foundOmissionBlock = true;
- } else if (foundIPv4TransitionBlock && i === blocks.length - 1) {
- // it has been checked before that the last
- // block is a valid IPv4 address
- } else if (!ipv6Block.test(blocks[i])) {
- return false;
- }
- }
- if (foundOmissionBlock) {
- return blocks.length >= 1;
- }
- return blocks.length === expectedNumberOfBlocks;
- }
- return false;
-}
-
-var default_url_options = {
- protocols: ['http', 'https', 'ftp'],
- require_tld: true,
- require_protocol: false,
- require_host: true,
- require_valid_protocol: true,
- allow_underscores: false,
- allow_trailing_dot: false,
- allow_protocol_relative_urls: false
-};
-
-var wrapped_ipv6 = /^\[([^\]]+)\](?::([0-9]+))?$/;
-
-function isRegExp(obj) {
- return Object.prototype.toString.call(obj) === '[object RegExp]';
-}
-
-function checkHost(host, matches) {
- for (var i = 0; i < matches.length; i++) {
- var match = matches[i];
- if (host === match || isRegExp(match) && match.test(host)) {
- return true;
- }
- }
- return false;
-}
-
-function isURL(url, options) {
- assertString(url);
- if (!url || url.length >= 2083 || /[\s<>]/.test(url)) {
- return false;
- }
- if (url.indexOf('mailto:') === 0) {
- return false;
- }
- options = merge(options, default_url_options);
- var protocol = void 0,
- auth = void 0,
- host = void 0,
- hostname = void 0,
- port = void 0,
- port_str = void 0,
- split = void 0,
- ipv6 = void 0;
-
- split = url.split('#');
- url = split.shift();
-
- split = url.split('?');
- url = split.shift();
-
- split = url.split('://');
- if (split.length > 1) {
- protocol = split.shift();
- if (options.require_valid_protocol && options.protocols.indexOf(protocol) === -1) {
- return false;
- }
- } else if (options.require_protocol) {
- return false;
- } else if (options.allow_protocol_relative_urls && url.substr(0, 2) === '//') {
- split[0] = url.substr(2);
- }
- url = split.join('://');
-
- if (url === '') {
- return false;
- }
-
- split = url.split('/');
- url = split.shift();
-
- if (url === '' && !options.require_host) {
- return true;
- }
-
- split = url.split('@');
- if (split.length > 1) {
- auth = split.shift();
- if (auth.indexOf(':') >= 0 && auth.split(':').length > 2) {
- return false;
- }
- }
- hostname = split.join('@');
-
- port_str = null;
- ipv6 = null;
- var ipv6_match = hostname.match(wrapped_ipv6);
- if (ipv6_match) {
- host = '';
- ipv6 = ipv6_match[1];
- port_str = ipv6_match[2] || null;
- } else {
- split = hostname.split(':');
- host = split.shift();
- if (split.length) {
- port_str = split.join(':');
- }
- }
-
- if (port_str !== null) {
- port = parseInt(port_str, 10);
- if (!/^[0-9]+$/.test(port_str) || port <= 0 || port > 65535) {
- return false;
- }
- }
-
- if (!isIP(host) && !isFQDN(host, options) && (!ipv6 || !isIP(ipv6, 6))) {
- return false;
- }
-
- host = host || ipv6;
-
- if (options.host_whitelist && !checkHost(host, options.host_whitelist)) {
- return false;
- }
- if (options.host_blacklist && checkHost(host, options.host_blacklist)) {
- return false;
- }
-
- return true;
-}
-
-var macAddress = /^([0-9a-fA-F][0-9a-fA-F]:){5}([0-9a-fA-F][0-9a-fA-F])$/;
-
-function isMACAddress(str) {
- assertString(str);
- return macAddress.test(str);
-}
-
-function isBoolean(str) {
- assertString(str);
- return ['true', 'false', '1', '0'].indexOf(str) >= 0;
-}
-
-var alpha = {
- 'en-US': /^[A-Z]+$/i,
- 'cs-CZ': /^[A-ZÁČĎÉĚÍŇÓŘŠŤÚŮÝŽ]+$/i,
- 'da-DK': /^[A-ZÆØÅ]+$/i,
- 'de-DE': /^[A-ZÄÖÜß]+$/i,
- 'es-ES': /^[A-ZÁÉÍÑÓÚÜ]+$/i,
- 'fr-FR': /^[A-ZÀÂÆÇÉÈÊËÏÎÔŒÙÛÜŸ]+$/i,
- 'it-IT': /^[A-ZÀÉÈÌÎÓÒÙ]+$/i,
- 'nb-NO': /^[A-ZÆØÅ]+$/i,
- 'nl-NL': /^[A-ZÁÉËÏÓÖÜÚ]+$/i,
- 'nn-NO': /^[A-ZÆØÅ]+$/i,
- 'hu-HU': /^[A-ZÁÉÍÓÖŐÚÜŰ]+$/i,
- 'pl-PL': /^[A-ZĄĆĘŚŁŃÓŻŹ]+$/i,
- 'pt-PT': /^[A-ZÃÁÀÂÇÉÊÍÕÓÔÚÜ]+$/i,
- 'ru-RU': /^[А-ЯЁ]+$/i,
- 'sr-RS@latin': /^[A-ZČĆŽŠĐ]+$/i,
- 'sr-RS': /^[А-ЯЂЈЉЊЋЏ]+$/i,
- 'sv-SE': /^[A-ZÅÄÖ]+$/i,
- 'tr-TR': /^[A-ZÇĞİıÖŞÜ]+$/i,
- 'uk-UA': /^[А-ЩЬЮЯЄIЇҐі]+$/i,
- ar: /^[ءآأؤإئابةتثجحخدذرزسشصضطظعغفقكلمنهوىيًٌٍَُِّْٰ]+$/
-};
-
-var alphanumeric = {
- 'en-US': /^[0-9A-Z]+$/i,
- 'cs-CZ': /^[0-9A-ZÁČĎÉĚÍŇÓŘŠŤÚŮÝŽ]+$/i,
- 'da-DK': /^[0-9A-ZÆØÅ]+$/i,
- 'de-DE': /^[0-9A-ZÄÖÜß]+$/i,
- 'es-ES': /^[0-9A-ZÁÉÍÑÓÚÜ]+$/i,
- 'fr-FR': /^[0-9A-ZÀÂÆÇÉÈÊËÏÎÔŒÙÛÜŸ]+$/i,
- 'it-IT': /^[0-9A-ZÀÉÈÌÎÓÒÙ]+$/i,
- 'hu-HU': /^[0-9A-ZÁÉÍÓÖŐÚÜŰ]+$/i,
- 'nb-NO': /^[0-9A-ZÆØÅ]+$/i,
- 'nl-NL': /^[0-9A-ZÁÉËÏÓÖÜÚ]+$/i,
- 'nn-NO': /^[0-9A-ZÆØÅ]+$/i,
- 'pl-PL': /^[0-9A-ZĄĆĘŚŁŃÓŻŹ]+$/i,
- 'pt-PT': /^[0-9A-ZÃÁÀÂÇÉÊÍÕÓÔÚÜ]+$/i,
- 'ru-RU': /^[0-9А-ЯЁ]+$/i,
- 'sr-RS@latin': /^[0-9A-ZČĆŽŠĐ]+$/i,
- 'sr-RS': /^[0-9А-ЯЂЈЉЊЋЏ]+$/i,
- 'sv-SE': /^[0-9A-ZÅÄÖ]+$/i,
- 'tr-TR': /^[0-9A-ZÇĞİıÖŞÜ]+$/i,
- 'uk-UA': /^[0-9А-ЩЬЮЯЄIЇҐі]+$/i,
- ar: /^[٠١٢٣٤٥٦٧٨٩0-9ءآأؤإئابةتثجحخدذرزسشصضطظعغفقكلمنهوىيًٌٍَُِّْٰ]+$/
-};
-
-var decimal = {
- 'en-US': '.',
- ar: '٫'
-};
-
-var englishLocales = ['AU', 'GB', 'HK', 'IN', 'NZ', 'ZA', 'ZM'];
-
-for (var locale, i = 0; i < englishLocales.length; i++) {
- locale = 'en-' + englishLocales[i];
- alpha[locale] = alpha['en-US'];
- alphanumeric[locale] = alphanumeric['en-US'];
- decimal[locale] = decimal['en-US'];
-}
-
-// Source: http://www.localeplanet.com/java/
-var arabicLocales = ['AE', 'BH', 'DZ', 'EG', 'IQ', 'JO', 'KW', 'LB', 'LY', 'MA', 'QM', 'QA', 'SA', 'SD', 'SY', 'TN', 'YE'];
-
-for (var _locale, _i = 0; _i < arabicLocales.length; _i++) {
- _locale = 'ar-' + arabicLocales[_i];
- alpha[_locale] = alpha.ar;
- alphanumeric[_locale] = alphanumeric.ar;
- decimal[_locale] = decimal.ar;
-}
-
-// Source: https://en.wikipedia.org/wiki/Decimal_mark
-var dotDecimal = [];
-var commaDecimal = ['cs-CZ', 'da-DK', 'de-DE', 'es-ES', 'fr-FR', 'it-IT', 'hu-HU', 'nb-NO', 'nn-NO', 'nl-NL', 'pl-Pl', 'pt-PT', 'ru-RU', 'sr-RS@latin', 'sr-RS', 'sv-SE', 'tr-TR', 'uk-UA'];
-
-for (var _i2 = 0; _i2 < dotDecimal.length; _i2++) {
- decimal[dotDecimal[_i2]] = decimal['en-US'];
-}
-
-for (var _i3 = 0; _i3 < commaDecimal.length; _i3++) {
- decimal[commaDecimal[_i3]] = ',';
-}
-
-alpha['pt-BR'] = alpha['pt-PT'];
-alphanumeric['pt-BR'] = alphanumeric['pt-PT'];
-decimal['pt-BR'] = decimal['pt-PT'];
-
-function isAlpha(str) {
- var locale = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'en-US';
-
- assertString(str);
- if (locale in alpha) {
- return alpha[locale].test(str);
- }
- throw new Error('Invalid locale \'' + locale + '\'');
-}
-
-function isAlphanumeric(str) {
- var locale = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'en-US';
-
- assertString(str);
- if (locale in alphanumeric) {
- return alphanumeric[locale].test(str);
- }
- throw new Error('Invalid locale \'' + locale + '\'');
-}
-
-var numeric = /^[-+]?[0-9]+$/;
-
-function isNumeric(str) {
- assertString(str);
- return numeric.test(str);
-}
-
-var int = /^(?:[-+]?(?:0|[1-9][0-9]*))$/;
-var intLeadingZeroes = /^[-+]?[0-9]+$/;
-
-function isInt(str, options) {
- assertString(str);
- options = options || {};
-
- // Get the regex to use for testing, based on whether
- // leading zeroes are allowed or not.
- var regex = options.hasOwnProperty('allow_leading_zeroes') && !options.allow_leading_zeroes ? int : intLeadingZeroes;
-
- // Check min/max/lt/gt
- var minCheckPassed = !options.hasOwnProperty('min') || str >= options.min;
- var maxCheckPassed = !options.hasOwnProperty('max') || str <= options.max;
- var ltCheckPassed = !options.hasOwnProperty('lt') || str < options.lt;
- var gtCheckPassed = !options.hasOwnProperty('gt') || str > options.gt;
-
- return regex.test(str) && minCheckPassed && maxCheckPassed && ltCheckPassed && gtCheckPassed;
-}
-
-function isPort(str) {
- return isInt(str, { min: 0, max: 65535 });
-}
-
-function isLowercase(str) {
- assertString(str);
- return str === str.toLowerCase();
-}
-
-function isUppercase(str) {
- assertString(str);
- return str === str.toUpperCase();
-}
-
-/* eslint-disable no-control-regex */
-var ascii = /^[\x00-\x7F]+$/;
-/* eslint-enable no-control-regex */
-
-function isAscii(str) {
- assertString(str);
- return ascii.test(str);
-}
-
-var fullWidth = /[^\u0020-\u007E\uFF61-\uFF9F\uFFA0-\uFFDC\uFFE8-\uFFEE0-9a-zA-Z]/;
-
-function isFullWidth(str) {
- assertString(str);
- return fullWidth.test(str);
-}
-
-var halfWidth = /[\u0020-\u007E\uFF61-\uFF9F\uFFA0-\uFFDC\uFFE8-\uFFEE0-9a-zA-Z]/;
-
-function isHalfWidth(str) {
- assertString(str);
- return halfWidth.test(str);
-}
-
-function isVariableWidth(str) {
- assertString(str);
- return fullWidth.test(str) && halfWidth.test(str);
-}
-
-/* eslint-disable no-control-regex */
-var multibyte = /[^\x00-\x7F]/;
-/* eslint-enable no-control-regex */
-
-function isMultibyte(str) {
- assertString(str);
- return multibyte.test(str);
-}
-
-var surrogatePair = /[\uD800-\uDBFF][\uDC00-\uDFFF]/;
-
-function isSurrogatePair(str) {
- assertString(str);
- return surrogatePair.test(str);
-}
-
-function isFloat(str, options) {
- assertString(str);
- options = options || {};
- var float = new RegExp('^(?:[-+])?(?:[0-9]+)?(?:\\' + (options.locale ? decimal[options.locale] : '.') + '[0-9]*)?(?:[eE][\\+\\-]?(?:[0-9]+))?$');
- if (str === '' || str === '.' || str === '-' || str === '+') {
- return false;
- }
- return float.test(str) && (!options.hasOwnProperty('min') || str >= options.min) && (!options.hasOwnProperty('max') || str <= options.max) && (!options.hasOwnProperty('lt') || str < options.lt) && (!options.hasOwnProperty('gt') || str > options.gt);
-}
-
-function decimalRegExp(options) {
- var regExp = new RegExp('^[-+]?([0-9]+)?(\\' + decimal[options.locale] + '[0-9]{' + options.decimal_digits + '})' + (options.force_decimal ? '' : '?') + '$');
- return regExp;
-}
-
-var default_decimal_options = {
- force_decimal: false,
- decimal_digits: '1,',
- locale: 'en-US'
-};
-
-var blacklist = ['', '-', '+'];
-
-function isDecimal(str, options) {
- assertString(str);
- options = merge(options, default_decimal_options);
- if (options.locale in decimal) {
- return !blacklist.includes(str.replace(/ /g, '')) && decimalRegExp(options).test(str);
- }
- throw new Error('Invalid locale \'' + options.locale + '\'');
-}
-
-var hexadecimal = /^[0-9A-F]+$/i;
-
-function isHexadecimal(str) {
- assertString(str);
- return hexadecimal.test(str);
-}
-
-function isDivisibleBy(str, num) {
- assertString(str);
- return toFloat(str) % parseInt(num, 10) === 0;
-}
-
-var hexcolor = /^#?([0-9A-F]{3}|[0-9A-F]{6})$/i;
-
-function isHexColor(str) {
- assertString(str);
- return hexcolor.test(str);
-}
-
-// see http://isrc.ifpi.org/en/isrc-standard/code-syntax
-var isrc = /^[A-Z]{2}[0-9A-Z]{3}\d{2}\d{5}$/;
-
-function isISRC(str) {
- assertString(str);
- return isrc.test(str);
-}
-
-var md5 = /^[a-f0-9]{32}$/;
-
-function isMD5(str) {
- assertString(str);
- return md5.test(str);
-}
-
-var lengths = {
- md5: 32,
- md4: 32,
- sha1: 40,
- sha256: 64,
- sha384: 96,
- sha512: 128,
- ripemd128: 32,
- ripemd160: 40,
- tiger128: 32,
- tiger160: 40,
- tiger192: 48,
- crc32: 8,
- crc32b: 8
-};
-
-function isHash(str, algorithm) {
- assertString(str);
- var hash = new RegExp('^[a-f0-9]{' + lengths[algorithm] + '}$');
- return hash.test(str);
-}
-
-function isJSON(str) {
- assertString(str);
- try {
- var obj = JSON.parse(str);
- return !!obj && (typeof obj === 'undefined' ? 'undefined' : _typeof(obj)) === 'object';
- } catch (e) {/* ignore */}
- return false;
-}
-
-function isEmpty(str) {
- assertString(str);
- return str.length === 0;
-}
-
-/* eslint-disable prefer-rest-params */
-function isLength(str, options) {
- assertString(str);
- var min = void 0;
- var max = void 0;
- if ((typeof options === 'undefined' ? 'undefined' : _typeof(options)) === 'object') {
- min = options.min || 0;
- max = options.max;
- } else {
- // backwards compatibility: isLength(str, min [, max])
- min = arguments[1];
- max = arguments[2];
- }
- var surrogatePairs = str.match(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g) || [];
- var len = str.length - surrogatePairs.length;
- return len >= min && (typeof max === 'undefined' || len <= max);
-}
-
-var uuid = {
- 3: /^[0-9A-F]{8}-[0-9A-F]{4}-3[0-9A-F]{3}-[0-9A-F]{4}-[0-9A-F]{12}$/i,
- 4: /^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i,
- 5: /^[0-9A-F]{8}-[0-9A-F]{4}-5[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i,
- all: /^[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}$/i
-};
-
-function isUUID(str) {
- var version = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'all';
-
- assertString(str);
- var pattern = uuid[version];
- return pattern && pattern.test(str);
-}
-
-function isMongoId(str) {
- assertString(str);
- return isHexadecimal(str) && str.length === 24;
-}
-
-function isAfter(str) {
- var date = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : String(new Date());
-
- assertString(str);
- var comparison = toDate(date);
- var original = toDate(str);
- return !!(original && comparison && original > comparison);
-}
-
-function isBefore(str) {
- var date = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : String(new Date());
-
- assertString(str);
- var comparison = toDate(date);
- var original = toDate(str);
- return !!(original && comparison && original < comparison);
-}
-
-function isIn(str, options) {
- assertString(str);
- var i = void 0;
- if (Object.prototype.toString.call(options) === '[object Array]') {
- var array = [];
- for (i in options) {
- if ({}.hasOwnProperty.call(options, i)) {
- array[i] = toString(options[i]);
- }
- }
- return array.indexOf(str) >= 0;
- } else if ((typeof options === 'undefined' ? 'undefined' : _typeof(options)) === 'object') {
- return options.hasOwnProperty(str);
- } else if (options && typeof options.indexOf === 'function') {
- return options.indexOf(str) >= 0;
- }
- return false;
-}
-
-/* eslint-disable max-len */
-var creditCard = /^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|(222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\d{3})\d{11}|62[0-9]{14})$/;
-/* eslint-enable max-len */
-
-function isCreditCard(str) {
- assertString(str);
- var sanitized = str.replace(/[- ]+/g, '');
- if (!creditCard.test(sanitized)) {
- return false;
- }
- var sum = 0;
- var digit = void 0;
- var tmpNum = void 0;
- var shouldDouble = void 0;
- for (var i = sanitized.length - 1; i >= 0; i--) {
- digit = sanitized.substring(i, i + 1);
- tmpNum = parseInt(digit, 10);
- if (shouldDouble) {
- tmpNum *= 2;
- if (tmpNum >= 10) {
- sum += tmpNum % 10 + 1;
- } else {
- sum += tmpNum;
- }
- } else {
- sum += tmpNum;
- }
- shouldDouble = !shouldDouble;
- }
- return !!(sum % 10 === 0 ? sanitized : false);
-}
-
-var isin = /^[A-Z]{2}[0-9A-Z]{9}[0-9]$/;
-
-function isISIN(str) {
- assertString(str);
- if (!isin.test(str)) {
- return false;
- }
-
- var checksumStr = str.replace(/[A-Z]/g, function (character) {
- return parseInt(character, 36);
- });
-
- var sum = 0;
- var digit = void 0;
- var tmpNum = void 0;
- var shouldDouble = true;
- for (var i = checksumStr.length - 2; i >= 0; i--) {
- digit = checksumStr.substring(i, i + 1);
- tmpNum = parseInt(digit, 10);
- if (shouldDouble) {
- tmpNum *= 2;
- if (tmpNum >= 10) {
- sum += tmpNum + 1;
- } else {
- sum += tmpNum;
- }
- } else {
- sum += tmpNum;
- }
- shouldDouble = !shouldDouble;
- }
-
- return parseInt(str.substr(str.length - 1), 10) === (10000 - sum) % 10;
-}
-
-var isbn10Maybe = /^(?:[0-9]{9}X|[0-9]{10})$/;
-var isbn13Maybe = /^(?:[0-9]{13})$/;
-var factor = [1, 3];
-
-function isISBN(str) {
- var version = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
-
- assertString(str);
- version = String(version);
- if (!version) {
- return isISBN(str, 10) || isISBN(str, 13);
- }
- var sanitized = str.replace(/[\s-]+/g, '');
- var checksum = 0;
- var i = void 0;
- if (version === '10') {
- if (!isbn10Maybe.test(sanitized)) {
- return false;
- }
- for (i = 0; i < 9; i++) {
- checksum += (i + 1) * sanitized.charAt(i);
- }
- if (sanitized.charAt(9) === 'X') {
- checksum += 10 * 10;
- } else {
- checksum += 10 * sanitized.charAt(9);
- }
- if (checksum % 11 === 0) {
- return !!sanitized;
- }
- } else if (version === '13') {
- if (!isbn13Maybe.test(sanitized)) {
- return false;
- }
- for (i = 0; i < 12; i++) {
- checksum += factor[i % 2] * sanitized.charAt(i);
- }
- if (sanitized.charAt(12) - (10 - checksum % 10) % 10 === 0) {
- return !!sanitized;
- }
- }
- return false;
-}
-
-var issn = '^\\d{4}-?\\d{3}[\\dX]$';
-
-function isISSN(str) {
- var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
-
- assertString(str);
- var testIssn = issn;
- testIssn = options.require_hyphen ? testIssn.replace('?', '') : testIssn;
- testIssn = options.case_sensitive ? new RegExp(testIssn) : new RegExp(testIssn, 'i');
- if (!testIssn.test(str)) {
- return false;
- }
- var issnDigits = str.replace('-', '');
- var position = 8;
- var checksum = 0;
- var _iteratorNormalCompletion = true;
- var _didIteratorError = false;
- var _iteratorError = undefined;
-
- try {
- for (var _iterator = issnDigits[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
- var digit = _step.value;
-
- var digitValue = digit.toUpperCase() === 'X' ? 10 : +digit;
- checksum += digitValue * position;
- --position;
- }
- } catch (err) {
- _didIteratorError = true;
- _iteratorError = err;
- } finally {
- try {
- if (!_iteratorNormalCompletion && _iterator.return) {
- _iterator.return();
- }
- } finally {
- if (_didIteratorError) {
- throw _iteratorError;
- }
- }
- }
-
- return checksum % 11 === 0;
-}
-
-/* eslint-disable max-len */
-var phones = {
- 'ar-AE': /^((\+?971)|0)?5[024568]\d{7}$/,
- 'ar-DZ': /^(\+?213|0)(5|6|7)\d{8}$/,
- 'ar-EG': /^((\+?20)|0)?1[012]\d{8}$/,
- 'ar-JO': /^(\+?962|0)?7[789]\d{7}$/,
- 'ar-SA': /^(!?(\+?966)|0)?5\d{8}$/,
- 'ar-SY': /^(!?(\+?963)|0)?9\d{8}$/,
- 'cs-CZ': /^(\+?420)? ?[1-9][0-9]{2} ?[0-9]{3} ?[0-9]{3}$/,
- 'da-DK': /^(\+?45)?\s?\d{2}\s?\d{2}\s?\d{2}\s?\d{2}$/,
- 'de-DE': /^(\+?49[ \.\-])?([\(]{1}[0-9]{1,6}[\)])?([0-9 \.\-\/]{3,20})((x|ext|extension)[ ]?[0-9]{1,4})?$/,
- 'el-GR': /^(\+?30)?(69\d{8})$/,
- 'en-AU': /^(\+?61|0)4\d{8}$/,
- 'en-GB': /^(\+?44|0)7\d{9}$/,
- 'en-HK': /^(\+?852\-?)?[456789]\d{3}\-?\d{4}$/,
- 'en-IN': /^(\+?91|0)?[789]\d{9}$/,
- 'en-KE': /^(\+?254|0)?[7]\d{8}$/,
- 'en-NG': /^(\+?234|0)?[789]\d{9}$/,
- 'en-NZ': /^(\+?64|0)2\d{7,9}$/,
- 'en-PK': /^((\+92)|(0092))-{0,1}\d{3}-{0,1}\d{7}$|^\d{11}$|^\d{4}-\d{7}$/,
- 'en-RW': /^(\+?250|0)?[7]\d{8}$/,
- 'en-SG': /^(\+65)?[89]\d{7}$/,
- 'en-TZ': /^(\+?255|0)?[67]\d{8}$/,
- 'en-UG': /^(\+?256|0)?[7]\d{8}$/,
- 'en-US': /^(\+?1)?[2-9]\d{2}[2-9](?!11)\d{6}$/,
- 'en-ZA': /^(\+?27|0)\d{9}$/,
- 'en-ZM': /^(\+?26)?09[567]\d{7}$/,
- 'es-ES': /^(\+?34)?(6\d{1}|7[1234])\d{7}$/,
- 'et-EE': /^(\+?372)?\s?(5|8[1-4])\s?([0-9]\s?){6,7}$/,
- 'fa-IR': /^(\+?98[\-\s]?|0)9[0-39]\d[\-\s]?\d{3}[\-\s]?\d{4}$/,
- 'fi-FI': /^(\+?358|0)\s?(4(0|1|2|4|5|6)?|50)\s?(\d\s?){4,8}\d$/,
- 'fo-FO': /^(\+?298)?\s?\d{2}\s?\d{2}\s?\d{2}$/,
- 'fr-FR': /^(\+?33|0)[67]\d{8}$/,
- 'he-IL': /^(\+972|0)([23489]|5[0248]|77)[1-9]\d{6}/,
- 'hu-HU': /^(\+?36)(20|30|70)\d{7}$/,
- 'id-ID': /^(\+?62|0[1-9])[\s|\d]+$/,
- 'it-IT': /^(\+?39)?\s?3\d{2} ?\d{6,7}$/,
- 'ja-JP': /^(\+?81|0)[789]0[ \-]?[1-9]\d{2}[ \-]?\d{5}$/,
- 'kl-GL': /^(\+?299)?\s?\d{2}\s?\d{2}\s?\d{2}$/,
- 'ko-KR': /^((\+?82)[ \-]?)?0?1([0|1|6|7|8|9]{1})[ \-]?\d{3,4}[ \-]?\d{4}$/,
- 'lt-LT': /^(\+370|8)\d{8}$/,
- 'ms-MY': /^(\+?6?01){1}(([145]{1}(\-|\s)?\d{7,8})|([236789]{1}(\s|\-)?\d{7}))$/,
- 'nb-NO': /^(\+?47)?[49]\d{7}$/,
- 'nl-BE': /^(\+?32|0)4?\d{8}$/,
- 'nn-NO': /^(\+?47)?[49]\d{7}$/,
- 'pl-PL': /^(\+?48)? ?[5-8]\d ?\d{3} ?\d{2} ?\d{2}$/,
- 'pt-BR': /^(\+?55|0)\-?[1-9]{2}\-?[2-9]{1}\d{3,4}\-?\d{4}$/,
- 'pt-PT': /^(\+?351)?9[1236]\d{7}$/,
- 'ro-RO': /^(\+?4?0)\s?7\d{2}(\/|\s|\.|\-)?\d{3}(\s|\.|\-)?\d{3}$/,
- 'ru-RU': /^(\+?7|8)?9\d{9}$/,
- 'sk-SK': /^(\+?421)? ?[1-9][0-9]{2} ?[0-9]{3} ?[0-9]{3}$/,
- 'sr-RS': /^(\+3816|06)[- \d]{5,9}$/,
- 'tr-TR': /^(\+?90|0)?5\d{9}$/,
- 'uk-UA': /^(\+?38|8)?0\d{9}$/,
- 'vi-VN': /^(\+?84|0)?((1(2([0-9])|6([2-9])|88|99))|(9((?!5)[0-9])))([0-9]{7})$/,
- 'zh-CN': /^(\+?0?86\-?)?1[345789]\d{9}$/,
- 'zh-TW': /^(\+?886\-?|0)?9\d{8}$/
-};
-/* eslint-enable max-len */
-
-// aliases
-phones['en-CA'] = phones['en-US'];
-phones['fr-BE'] = phones['nl-BE'];
-phones['zh-HK'] = phones['en-HK'];
-
-function isMobilePhone(str, locale) {
- assertString(str);
- if (locale in phones) {
- return phones[locale].test(str);
- } else if (locale === 'any') {
- for (var key in phones) {
- if (phones.hasOwnProperty(key)) {
- var phone = phones[key];
- if (phone.test(str)) {
- return true;
- }
- }
- }
- return false;
- }
- throw new Error('Invalid locale \'' + locale + '\'');
-}
-
-function currencyRegex(options) {
- var decimal_digits = '\\d{' + options.digits_after_decimal[0] + '}';
- options.digits_after_decimal.forEach(function (digit, index) {
- if (index !== 0) decimal_digits = decimal_digits + '|\\d{' + digit + '}';
- });
- var symbol = '(\\' + options.symbol.replace(/\./g, '\\.') + ')' + (options.require_symbol ? '' : '?'),
- negative = '-?',
- whole_dollar_amount_without_sep = '[1-9]\\d*',
- whole_dollar_amount_with_sep = '[1-9]\\d{0,2}(\\' + options.thousands_separator + '\\d{3})*',
- valid_whole_dollar_amounts = ['0', whole_dollar_amount_without_sep, whole_dollar_amount_with_sep],
- whole_dollar_amount = '(' + valid_whole_dollar_amounts.join('|') + ')?',
- decimal_amount = '(\\' + options.decimal_separator + '(' + decimal_digits + '))' + (options.require_decimal ? '' : '?');
- var pattern = whole_dollar_amount + (options.allow_decimal || options.require_decimal ? decimal_amount : '');
-
- // default is negative sign before symbol, but there are two other options (besides parens)
- if (options.allow_negatives && !options.parens_for_negatives) {
- if (options.negative_sign_after_digits) {
- pattern += negative;
- } else if (options.negative_sign_before_digits) {
- pattern = negative + pattern;
- }
- }
-
- // South African Rand, for example, uses R 123 (space) and R-123 (no space)
- if (options.allow_negative_sign_placeholder) {
- pattern = '( (?!\\-))?' + pattern;
- } else if (options.allow_space_after_symbol) {
- pattern = ' ?' + pattern;
- } else if (options.allow_space_after_digits) {
- pattern += '( (?!$))?';
- }
-
- if (options.symbol_after_digits) {
- pattern += symbol;
- } else {
- pattern = symbol + pattern;
- }
-
- if (options.allow_negatives) {
- if (options.parens_for_negatives) {
- pattern = '(\\(' + pattern + '\\)|' + pattern + ')';
- } else if (!(options.negative_sign_before_digits || options.negative_sign_after_digits)) {
- pattern = negative + pattern;
- }
- }
-
- // ensure there's a dollar and/or decimal amount, and that
- // it doesn't start with a space or a negative sign followed by a space
- return new RegExp('^(?!-? )(?=.*\\d)' + pattern + '$');
-}
-
-var default_currency_options = {
- symbol: '$',
- require_symbol: false,
- allow_space_after_symbol: false,
- symbol_after_digits: false,
- allow_negatives: true,
- parens_for_negatives: false,
- negative_sign_before_digits: false,
- negative_sign_after_digits: false,
- allow_negative_sign_placeholder: false,
- thousands_separator: ',',
- decimal_separator: '.',
- allow_decimal: true,
- require_decimal: false,
- digits_after_decimal: [2],
- allow_space_after_digits: false
-};
-
-function isCurrency(str, options) {
- assertString(str);
- options = merge(options, default_currency_options);
- return currencyRegex(options).test(str);
-}
-
-/* eslint-disable max-len */
-// from http://goo.gl/0ejHHW
-var iso8601 = /^([\+-]?\d{4}(?!\d{2}\b))((-?)((0[1-9]|1[0-2])(\3([12]\d|0[1-9]|3[01]))?|W([0-4]\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\d|[12]\d{2}|3([0-5]\d|6[1-6])))([T\s]((([01]\d|2[0-3])((:?)[0-5]\d)?|24:?00)([\.,]\d+(?!:))?)?(\17[0-5]\d([\.,]\d+)?)?([zZ]|([\+-])([01]\d|2[0-3]):?([0-5]\d)?)?)?)?$/;
-/* eslint-enable max-len */
-
-function isISO8601(str) {
- assertString(str);
- return iso8601.test(str);
-}
-
-// from https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2
-var validISO31661Alpha2CountriesCodes = ['AD', 'AE', 'AF', 'AG', 'AI', 'AL', 'AM', 'AO', 'AQ', 'AR', 'AS', 'AT', 'AU', 'AW', 'AX', 'AZ', 'BA', 'BB', 'BD', 'BE', 'BF', 'BG', 'BH', 'BI', 'BJ', 'BL', 'BM', 'BN', 'BO', 'BQ', 'BR', 'BS', 'BT', 'BV', 'BW', 'BY', 'BZ', 'CA', 'CC', 'CD', 'CF', 'CG', 'CH', 'CI', 'CK', 'CL', 'CM', 'CN', 'CO', 'CR', 'CU', 'CV', 'CW', 'CX', 'CY', 'CZ', 'DE', 'DJ', 'DK', 'DM', 'DO', 'DZ', 'EC', 'EE', 'EG', 'EH', 'ER', 'ES', 'ET', 'FI', 'FJ', 'FK', 'FM', 'FO', 'FR', 'GA', 'GB', 'GD', 'GE', 'GF', 'GG', 'GH', 'GI', 'GL', 'GM', 'GN', 'GP', 'GQ', 'GR', 'GS', 'GT', 'GU', 'GW', 'GY', 'HK', 'HM', 'HN', 'HR', 'HT', 'HU', 'ID', 'IE', 'IL', 'IM', 'IN', 'IO', 'IQ', 'IR', 'IS', 'IT', 'JE', 'JM', 'JO', 'JP', 'KE', 'KG', 'KH', 'KI', 'KM', 'KN', 'KP', 'KR', 'KW', 'KY', 'KZ', 'LA', 'LB', 'LC', 'LI', 'LK', 'LR', 'LS', 'LT', 'LU', 'LV', 'LY', 'MA', 'MC', 'MD', 'ME', 'MF', 'MG', 'MH', 'MK', 'ML', 'MM', 'MN', 'MO', 'MP', 'MQ', 'MR', 'MS', 'MT', 'MU', 'MV', 'MW', 'MX', 'MY', 'MZ', 'NA', 'NC', 'NE', 'NF', 'NG', 'NI', 'NL', 'NO', 'NP', 'NR', 'NU', 'NZ', 'OM', 'PA', 'PE', 'PF', 'PG', 'PH', 'PK', 'PL', 'PM', 'PN', 'PR', 'PS', 'PT', 'PW', 'PY', 'QA', 'RE', 'RO', 'RS', 'RU', 'RW', 'SA', 'SB', 'SC', 'SD', 'SE', 'SG', 'SH', 'SI', 'SJ', 'SK', 'SL', 'SM', 'SN', 'SO', 'SR', 'SS', 'ST', 'SV', 'SX', 'SY', 'SZ', 'TC', 'TD', 'TF', 'TG', 'TH', 'TJ', 'TK', 'TL', 'TM', 'TN', 'TO', 'TR', 'TT', 'TV', 'TW', 'TZ', 'UA', 'UG', 'UM', 'US', 'UY', 'UZ', 'VA', 'VC', 'VE', 'VG', 'VI', 'VN', 'VU', 'WF', 'WS', 'YE', 'YT', 'ZA', 'ZM', 'ZW'];
-
-function isISO31661Alpha2(str) {
- assertString(str);
- return validISO31661Alpha2CountriesCodes.includes(str.toUpperCase());
-}
-
-var notBase64 = /[^A-Z0-9+\/=]/i;
-
-function isBase64(str) {
- assertString(str);
- var len = str.length;
- if (!len || len % 4 !== 0 || notBase64.test(str)) {
- return false;
- }
- var firstPaddingChar = str.indexOf('=');
- return firstPaddingChar === -1 || firstPaddingChar === len - 1 || firstPaddingChar === len - 2 && str[len - 1] === '=';
-}
-
-var dataURI = /^\s*data:([a-z]+\/[a-z0-9\-\+]+(;[a-z\-]+=[a-z0-9\-]+)?)?(;base64)?,[a-z0-9!\$&',\(\)\*\+,;=\-\._~:@\/\?%\s]*\s*$/i; // eslint-disable-line max-len
-
-function isDataURI(str) {
- assertString(str);
- return dataURI.test(str);
-}
-
-var lat = /^\(?[+-]?(90(\.0+)?|[1-8]?\d(\.\d+)?)$/;
-var long = /^\s?[+-]?(180(\.0+)?|1[0-7]\d(\.\d+)?|\d{1,2}(\.\d+)?)\)?$/;
-
-var isLatLong = function (str) {
- assertString(str);
- if (!str.includes(',')) return false;
- var pair = str.split(',');
- return lat.test(pair[0]) && long.test(pair[1]);
-};
-
-// common patterns
-var threeDigit = /^\d{3}$/;
-var fourDigit = /^\d{4}$/;
-var fiveDigit = /^\d{5}$/;
-var sixDigit = /^\d{6}$/;
-
-var patterns = {
- AT: fourDigit,
- AU: fourDigit,
- BE: fourDigit,
- CA: /^[ABCEGHJKLMNPRSTVXY]\d[ABCEGHJ-NPRSTV-Z][\s\-]?\d[ABCEGHJ-NPRSTV-Z]\d$/i,
- CH: fourDigit,
- CZ: /^\d{3}\s?\d{2}$/,
- DE: fiveDigit,
- DK: fourDigit,
- DZ: fiveDigit,
- ES: fiveDigit,
- FI: fiveDigit,
- FR: /^\d{2}\s?\d{3}$/,
- GB: /^(gir\s?0aa|[a-z]{1,2}\d[\da-z]?\s?(\d[a-z]{2})?)$/i,
- GR: /^\d{3}\s?\d{2}$/,
- IL: fiveDigit,
- IN: sixDigit,
- IS: threeDigit,
- IT: fiveDigit,
- JP: /^\d{3}\-\d{4}$/,
- KE: fiveDigit,
- LI: /^(948[5-9]|949[0-7])$/,
- MX: fiveDigit,
- NL: /^\d{4}\s?[a-z]{2}$/i,
- NO: fourDigit,
- PL: /^\d{2}\-\d{3}$/,
- PT: /^\d{4}(\-\d{3})?$/,
- RO: sixDigit,
- RU: sixDigit,
- SA: fiveDigit,
- SE: /^\d{3}\s?\d{2}$/,
- TW: /^\d{3}(\d{2})?$/,
- US: /^\d{5}(-\d{4})?$/,
- ZA: fourDigit,
- ZM: fiveDigit
-};
-
-
-
-var isPostalCode = function (str, locale) {
- assertString(str);
- if (locale in patterns) {
- return patterns[locale].test(str);
- } else if (locale === 'any') {
- for (var key in patterns) {
- if (patterns.hasOwnProperty(key)) {
- var pattern = patterns[key];
- if (pattern.test(str)) {
- return true;
- }
- }
- }
- return false;
- }
- throw new Error('Invalid locale \'' + locale + '\'');
-};
-
-function ltrim(str, chars) {
- assertString(str);
- var pattern = chars ? new RegExp('^[' + chars + ']+', 'g') : /^\s+/g;
- return str.replace(pattern, '');
-}
-
-function rtrim(str, chars) {
- assertString(str);
- var pattern = chars ? new RegExp('[' + chars + ']') : /\s/;
-
- var idx = str.length - 1;
- while (idx >= 0 && pattern.test(str[idx])) {
- idx--;
- }
-
- return idx < str.length ? str.substr(0, idx + 1) : str;
-}
-
-function trim(str, chars) {
- return rtrim(ltrim(str, chars), chars);
-}
-
-function escape(str) {
- assertString(str);
- return str.replace(/&/g, '&').replace(/"/g, '"').replace(/'/g, ''').replace(//g, '>').replace(/\//g, '/').replace(/\\/g, '\').replace(/`/g, '`');
-}
-
-function unescape(str) {
- assertString(str);
- return str.replace(/&/g, '&').replace(/"/g, '"').replace(/'/g, "'").replace(/</g, '<').replace(/>/g, '>').replace(///g, '/').replace(/\/g, '\\').replace(/`/g, '`');
-}
-
-function blacklist$1(str, chars) {
- assertString(str);
- return str.replace(new RegExp('[' + chars + ']+', 'g'), '');
-}
-
-function stripLow(str, keep_new_lines) {
- assertString(str);
- var chars = keep_new_lines ? '\\x00-\\x09\\x0B\\x0C\\x0E-\\x1F\\x7F' : '\\x00-\\x1F\\x7F';
- return blacklist$1(str, chars);
-}
-
-function whitelist(str, chars) {
- assertString(str);
- return str.replace(new RegExp('[^' + chars + ']+', 'g'), '');
-}
-
-function isWhitelisted(str, chars) {
- assertString(str);
- for (var i = str.length - 1; i >= 0; i--) {
- if (chars.indexOf(str[i]) === -1) {
- return false;
- }
- }
- return true;
-}
-
-var default_normalize_email_options = {
- // The following options apply to all email addresses
- // Lowercases the local part of the email address.
- // Please note this may violate RFC 5321 as per http://stackoverflow.com/a/9808332/192024).
- // The domain is always lowercased, as per RFC 1035
- all_lowercase: true,
-
- // The following conversions are specific to GMail
- // Lowercases the local part of the GMail address (known to be case-insensitive)
- gmail_lowercase: true,
- // Removes dots from the local part of the email address, as that's ignored by GMail
- gmail_remove_dots: true,
- // Removes the subaddress (e.g. "+foo") from the email address
- gmail_remove_subaddress: true,
- // Conversts the googlemail.com domain to gmail.com
- gmail_convert_googlemaildotcom: true,
-
- // The following conversions are specific to Outlook.com / Windows Live / Hotmail
- // Lowercases the local part of the Outlook.com address (known to be case-insensitive)
- outlookdotcom_lowercase: true,
- // Removes the subaddress (e.g. "+foo") from the email address
- outlookdotcom_remove_subaddress: true,
-
- // The following conversions are specific to Yahoo
- // Lowercases the local part of the Yahoo address (known to be case-insensitive)
- yahoo_lowercase: true,
- // Removes the subaddress (e.g. "-foo") from the email address
- yahoo_remove_subaddress: true,
-
- // The following conversions are specific to iCloud
- // Lowercases the local part of the iCloud address (known to be case-insensitive)
- icloud_lowercase: true,
- // Removes the subaddress (e.g. "+foo") from the email address
- icloud_remove_subaddress: true
-};
-
-// List of domains used by iCloud
-var icloud_domains = ['icloud.com', 'me.com'];
-
-// List of domains used by Outlook.com and its predecessors
-// This list is likely incomplete.
-// Partial reference:
-// https://blogs.office.com/2013/04/17/outlook-com-gets-two-step-verification-sign-in-by-alias-and-new-international-domains/
-var outlookdotcom_domains = ['hotmail.at', 'hotmail.be', 'hotmail.ca', 'hotmail.cl', 'hotmail.co.il', 'hotmail.co.nz', 'hotmail.co.th', 'hotmail.co.uk', 'hotmail.com', 'hotmail.com.ar', 'hotmail.com.au', 'hotmail.com.br', 'hotmail.com.gr', 'hotmail.com.mx', 'hotmail.com.pe', 'hotmail.com.tr', 'hotmail.com.vn', 'hotmail.cz', 'hotmail.de', 'hotmail.dk', 'hotmail.es', 'hotmail.fr', 'hotmail.hu', 'hotmail.id', 'hotmail.ie', 'hotmail.in', 'hotmail.it', 'hotmail.jp', 'hotmail.kr', 'hotmail.lv', 'hotmail.my', 'hotmail.ph', 'hotmail.pt', 'hotmail.sa', 'hotmail.sg', 'hotmail.sk', 'live.be', 'live.co.uk', 'live.com', 'live.com.ar', 'live.com.mx', 'live.de', 'live.es', 'live.eu', 'live.fr', 'live.it', 'live.nl', 'msn.com', 'outlook.at', 'outlook.be', 'outlook.cl', 'outlook.co.il', 'outlook.co.nz', 'outlook.co.th', 'outlook.com', 'outlook.com.ar', 'outlook.com.au', 'outlook.com.br', 'outlook.com.gr', 'outlook.com.pe', 'outlook.com.tr', 'outlook.com.vn', 'outlook.cz', 'outlook.de', 'outlook.dk', 'outlook.es', 'outlook.fr', 'outlook.hu', 'outlook.id', 'outlook.ie', 'outlook.in', 'outlook.it', 'outlook.jp', 'outlook.kr', 'outlook.lv', 'outlook.my', 'outlook.ph', 'outlook.pt', 'outlook.sa', 'outlook.sg', 'outlook.sk', 'passport.com'];
-
-// List of domains used by Yahoo Mail
-// This list is likely incomplete
-var yahoo_domains = ['rocketmail.com', 'yahoo.ca', 'yahoo.co.uk', 'yahoo.com', 'yahoo.de', 'yahoo.fr', 'yahoo.in', 'yahoo.it', 'ymail.com'];
-
-function normalizeEmail(email, options) {
- options = merge(options, default_normalize_email_options);
-
- var raw_parts = email.split('@');
- var domain = raw_parts.pop();
- var user = raw_parts.join('@');
- var parts = [user, domain];
-
- // The domain is always lowercased, as it's case-insensitive per RFC 1035
- parts[1] = parts[1].toLowerCase();
-
- if (parts[1] === 'gmail.com' || parts[1] === 'googlemail.com') {
- // Address is GMail
- if (options.gmail_remove_subaddress) {
- parts[0] = parts[0].split('+')[0];
- }
- if (options.gmail_remove_dots) {
- parts[0] = parts[0].replace(/\./g, '');
- }
- if (!parts[0].length) {
- return false;
- }
- if (options.all_lowercase || options.gmail_lowercase) {
- parts[0] = parts[0].toLowerCase();
- }
- parts[1] = options.gmail_convert_googlemaildotcom ? 'gmail.com' : parts[1];
- } else if (~icloud_domains.indexOf(parts[1])) {
- // Address is iCloud
- if (options.icloud_remove_subaddress) {
- parts[0] = parts[0].split('+')[0];
- }
- if (!parts[0].length) {
- return false;
- }
- if (options.all_lowercase || options.icloud_lowercase) {
- parts[0] = parts[0].toLowerCase();
- }
- } else if (~outlookdotcom_domains.indexOf(parts[1])) {
- // Address is Outlook.com
- if (options.outlookdotcom_remove_subaddress) {
- parts[0] = parts[0].split('+')[0];
- }
- if (!parts[0].length) {
- return false;
- }
- if (options.all_lowercase || options.outlookdotcom_lowercase) {
- parts[0] = parts[0].toLowerCase();
- }
- } else if (~yahoo_domains.indexOf(parts[1])) {
- // Address is Yahoo
- if (options.yahoo_remove_subaddress) {
- var components = parts[0].split('-');
- parts[0] = components.length > 1 ? components.slice(0, -1).join('-') : components[0];
- }
- if (!parts[0].length) {
- return false;
- }
- if (options.all_lowercase || options.yahoo_lowercase) {
- parts[0] = parts[0].toLowerCase();
- }
- } else if (options.all_lowercase) {
- // Any other address
- parts[0] = parts[0].toLowerCase();
- }
- return parts.join('@');
-}
-
-var version = '9.1.2';
-
-var validator = {
- version: version,
- toDate: toDate,
- toFloat: toFloat,
- toInt: toInt,
- toBoolean: toBoolean,
- equals: equals,
- contains: contains,
- matches: matches,
- isEmail: isEmail,
- isURL: isURL,
- isMACAddress: isMACAddress,
- isIP: isIP,
- isFQDN: isFQDN,
- isBoolean: isBoolean,
- isAlpha: isAlpha,
- isAlphanumeric: isAlphanumeric,
- isNumeric: isNumeric,
- isPort: isPort,
- isLowercase: isLowercase,
- isUppercase: isUppercase,
- isAscii: isAscii,
- isFullWidth: isFullWidth,
- isHalfWidth: isHalfWidth,
- isVariableWidth: isVariableWidth,
- isMultibyte: isMultibyte,
- isSurrogatePair: isSurrogatePair,
- isInt: isInt,
- isFloat: isFloat,
- isDecimal: isDecimal,
- isHexadecimal: isHexadecimal,
- isDivisibleBy: isDivisibleBy,
- isHexColor: isHexColor,
- isISRC: isISRC,
- isMD5: isMD5,
- isHash: isHash,
- isJSON: isJSON,
- isEmpty: isEmpty,
- isLength: isLength,
- isByteLength: isByteLength,
- isUUID: isUUID,
- isMongoId: isMongoId,
- isAfter: isAfter,
- isBefore: isBefore,
- isIn: isIn,
- isCreditCard: isCreditCard,
- isISIN: isISIN,
- isISBN: isISBN,
- isISSN: isISSN,
- isMobilePhone: isMobilePhone,
- isPostalCode: isPostalCode,
- isCurrency: isCurrency,
- isISO8601: isISO8601,
- isISO31661Alpha2: isISO31661Alpha2,
- isBase64: isBase64,
- isDataURI: isDataURI,
- isLatLong: isLatLong,
- ltrim: ltrim,
- rtrim: rtrim,
- trim: trim,
- escape: escape,
- unescape: unescape,
- stripLow: stripLow,
- whitelist: whitelist,
- blacklist: blacklist$1,
- isWhitelisted: isWhitelisted,
- normalizeEmail: normalizeEmail,
- toString: toString
-};
-
-return validator;
-
-})));
diff --git a/validator.min.js b/validator.min.js
deleted file mode 100644
index 11457d99d..000000000
--- a/validator.min.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/*!
- * Copyright (c) 2016 Chris O'Hara
- *
- * 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.
- */
-!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.validator=t()}(this,function(){"use strict";function e(e){if(!("string"==typeof e||e instanceof String))throw new TypeError("This library (validator.js) validates strings only")}function t(t){return e(t),t=Date.parse(t),isNaN(t)?null:new Date(t)}function r(t){return e(t),parseFloat(t)}function o(e){return"object"===(void 0===e?"undefined":_(e))&&null!==e?e="function"==typeof e.toString?e.toString():"[object Object]":(null===e||void 0===e||isNaN(e)&&!e.length)&&(e=""),String(e)}function i(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=arguments[1];for(var r in t)void 0===e[r]&&(e[r]=t[r]);return e}function n(t,r){e(t);var o=void 0,i=void 0;"object"===(void 0===r?"undefined":_(r))?(o=r.min||0,i=r.max):(o=arguments[1],i=arguments[2]);var n=encodeURI(t).split(/%..|./).length-1;return n>=o&&(void 0===i||n<=i)}function a(t,r){e(t),(r=i(r,v)).allow_trailing_dot&&"."===t[t.length-1]&&(t=t.substring(0,t.length-1));var o=t.split(".");if(r.require_tld){var n=o.pop();if(!o.length||!/^([a-z\u00a1-\uffff]{2,}|xn[a-z0-9-]{2,})$/i.test(n))return!1;if(/[\s\u2002-\u200B\u202F\u205F\u3000\uFEFF\uDB40\uDC20]/.test(n))return!1}for(var a,l=0;l1&&void 0!==arguments[1]?arguments[1]:"";if(e(t),!(r=String(r)))return l(t,4)||l(t,6);if("4"===r){if(!E.test(t))return!1;return t.split(".").sort(function(e,t){return e-t})[3]<=255}if("6"===r){var o=t.split(":"),i=!1,n=l(o[o.length-1],4),a=n?7:8;if(o.length>a)return!1;if("::"===t)return!0;"::"===t.substr(0,2)?(o.shift(),o.shift(),i=!0):"::"===t.substr(t.length-2)&&(o.pop(),o.pop(),i=!0);for(var s=0;s0&&s=1:o.length===a}return!1}function s(e){return"[object RegExp]"===Object.prototype.toString.call(e)}function u(e,t){for(var r=0;r=r.min,n=!r.hasOwnProperty("max")||t<=r.max,a=!r.hasOwnProperty("lt")||tr.gt;return o.test(t)&&i&&n&&a&&l}function c(t){return e(t),Q.test(t)}function f(t){var r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";if(e(t),!(r=String(r)))return f(t,10)||f(t,13);var o=t.replace(/[\s-]+/g,""),i=0,n=void 0;if("10"===r){if(!ae.test(o))return!1;for(n=0;n<9;n++)i+=(n+1)*o.charAt(n);if("X"===o.charAt(9)?i+=100:i+=10*o.charAt(9),i%11==0)return!!o}else if("13"===r){if(!le.test(o))return!1;for(n=0;n<12;n++)i+=se[n%2]*o.charAt(n);if(o.charAt(12)-(10-i%10)%10==0)return!!o}return!1}function p(t,r){e(t);var o=r?new RegExp("^["+r+"]+","g"):/^\s+/g;return t.replace(o,"")}function g(t,r){e(t);for(var o=r?new RegExp("["+r+"]"):/\s/,i=t.length-1;i>=0&&o.test(t[i]);)i--;return i$/i,A=/^[a-z\d!#\$%&'\*\+\-\/=\?\^_`{\|}~]+$/i,x=/^([\s\x01-\x08\x0b\x0c\x0e-\x1f\x7f\x21\x23-\x5b\x5d-\x7e]|(\\[\x01-\x09\x0b\x0c\x0d-\x7f]))*$/i,S=/^[a-z\d!#\$%&'\*\+\-\/=\?\^_`{\|}~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+$/i,w=/^([\s\x01-\x08\x0b\x0c\x0e-\x1f\x7f\x21\x23-\x5b\x5d-\x7e\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|(\\[\x01-\x09\x0b\x0c\x0d-\x7f\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))*$/i,E=/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/,y=/^[0-9A-F]{1,4}$/i,b={protocols:["http","https","ftp"],require_tld:!0,require_protocol:!1,require_host:!0,require_valid_protocol:!0,allow_underscores:!1,allow_trailing_dot:!1,allow_protocol_relative_urls:!1},Z=/^\[([^\]]+)\](?::([0-9]+))?$/,R=/^([0-9a-fA-F][0-9a-fA-F]:){5}([0-9a-fA-F][0-9a-fA-F])$/,C={"en-US":/^[A-Z]+$/i,"cs-CZ":/^[A-ZÁČĎÉĚÍŇÓŘŠŤÚŮÝŽ]+$/i,"da-DK":/^[A-ZÆØÅ]+$/i,"de-DE":/^[A-ZÄÖÜß]+$/i,"es-ES":/^[A-ZÁÉÍÑÓÚÜ]+$/i,"fr-FR":/^[A-ZÀÂÆÇÉÈÊËÏÎÔŒÙÛÜŸ]+$/i,"it-IT":/^[A-ZÀÉÈÌÎÓÒÙ]+$/i,"nb-NO":/^[A-ZÆØÅ]+$/i,"nl-NL":/^[A-ZÁÉËÏÓÖÜÚ]+$/i,"nn-NO":/^[A-ZÆØÅ]+$/i,"hu-HU":/^[A-ZÁÉÍÓÖŐÚÜŰ]+$/i,"pl-PL":/^[A-ZĄĆĘŚŁŃÓŻŹ]+$/i,"pt-PT":/^[A-ZÃÁÀÂÇÉÊÍÕÓÔÚÜ]+$/i,"ru-RU":/^[А-ЯЁ]+$/i,"sr-RS@latin":/^[A-ZČĆŽŠĐ]+$/i,"sr-RS":/^[А-ЯЂЈЉЊЋЏ]+$/i,"sv-SE":/^[A-ZÅÄÖ]+$/i,"tr-TR":/^[A-ZÇĞİıÖŞÜ]+$/i,"uk-UA":/^[А-ЩЬЮЯЄIЇҐі]+$/i,ar:/^[ءآأؤإئابةتثجحخدذرزسشصضطظعغفقكلمنهوىيًٌٍَُِّْٰ]+$/},D={"en-US":/^[0-9A-Z]+$/i,"cs-CZ":/^[0-9A-ZÁČĎÉĚÍŇÓŘŠŤÚŮÝŽ]+$/i,"da-DK":/^[0-9A-ZÆØÅ]+$/i,"de-DE":/^[0-9A-ZÄÖÜß]+$/i,"es-ES":/^[0-9A-ZÁÉÍÑÓÚÜ]+$/i,"fr-FR":/^[0-9A-ZÀÂÆÇÉÈÊËÏÎÔŒÙÛÜŸ]+$/i,"it-IT":/^[0-9A-ZÀÉÈÌÎÓÒÙ]+$/i,"hu-HU":/^[0-9A-ZÁÉÍÓÖŐÚÜŰ]+$/i,"nb-NO":/^[0-9A-ZÆØÅ]+$/i,"nl-NL":/^[0-9A-ZÁÉËÏÓÖÜÚ]+$/i,"nn-NO":/^[0-9A-ZÆØÅ]+$/i,"pl-PL":/^[0-9A-ZĄĆĘŚŁŃÓŻŹ]+$/i,"pt-PT":/^[0-9A-ZÃÁÀÂÇÉÊÍÕÓÔÚÜ]+$/i,"ru-RU":/^[0-9А-ЯЁ]+$/i,"sr-RS@latin":/^[0-9A-ZČĆŽŠĐ]+$/i,"sr-RS":/^[0-9А-ЯЂЈЉЊЋЏ]+$/i,"sv-SE":/^[0-9A-ZÅÄÖ]+$/i,"tr-TR":/^[0-9A-ZÇĞİıÖŞÜ]+$/i,"uk-UA":/^[0-9А-ЩЬЮЯЄIЇҐі]+$/i,ar:/^[٠١٢٣٤٥٦٧٨٩0-9ءآأؤإئابةتثجحخدذرزسشصضطظعغفقكلمنهوىيًٌٍَُِّْٰ]+$/},I={"en-US":".",ar:"٫"},O=["AU","GB","HK","IN","NZ","ZA","ZM"],N=0;N=0},matches:function(t,r,o){return e(t),"[object RegExp]"!==Object.prototype.toString.call(r)&&(r=new RegExp(r,o)),r.test(t)},isEmail:function(t,r){if(e(t),(r=i(r,$)).require_display_name||r.allow_display_name){var o=t.match(F);if(o)t=o[1];else if(r.require_display_name)return!1}var l=t.split("@"),s=l.pop(),u=l.join("@"),d=s.toLowerCase();if("gmail.com"!==d&&"googlemail.com"!==d||(u=u.replace(/\./g,"").toLowerCase()),!n(u,{max:64})||!n(s,{max:254}))return!1;if(!a(s,{require_tld:r.require_tld}))return!1;if('"'===u[0])return u=u.slice(1,u.length-1),r.allow_utf8_local_part?w.test(u):x.test(u);for(var c=r.allow_utf8_local_part?S:A,f=u.split("."),p=0;p=2083||/[\s<>]/.test(t))return!1;if(0===t.indexOf("mailto:"))return!1;r=i(r,b);var o=void 0,n=void 0,s=void 0,d=void 0,c=void 0,f=void 0,p=void 0,g=void 0;if(p=t.split("#"),t=p.shift(),p=t.split("?"),t=p.shift(),(p=t.split("://")).length>1){if(o=p.shift(),r.require_valid_protocol&&-1===r.protocols.indexOf(o))return!1}else{if(r.require_protocol)return!1;r.allow_protocol_relative_urls&&"//"===t.substr(0,2)&&(p[0]=t.substr(2))}if(""===(t=p.join("://")))return!1;if(p=t.split("/"),""===(t=p.shift())&&!r.require_host)return!0;if((p=t.split("@")).length>1&&(n=p.shift()).indexOf(":")>=0&&n.split(":").length>2)return!1;f=null,g=null;var h=(d=p.join("@")).match(Z);return h?(s="",g=h[1],f=h[2]||null):(s=(p=d.split(":")).shift(),p.length&&(f=p.join(":"))),!(null!==f&&(c=parseInt(f,10),!/^[0-9]+$/.test(f)||c<=0||c>65535)||!(l(s)||a(s,r)||g&&l(g,6))||(s=s||g,r.host_whitelist&&!u(s,r.host_whitelist)||r.host_blacklist&&u(s,r.host_blacklist)))},isMACAddress:function(t){return e(t),R.test(t)},isIP:l,isFQDN:a,isBoolean:function(t){return e(t),["true","false","1","0"].indexOf(t)>=0},isAlpha:function(t){var r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"en-US";if(e(t),r in C)return C[r].test(t);throw new Error("Invalid locale '"+r+"'")},isAlphanumeric:function(t){var r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"en-US";if(e(t),r in D)return D[r].test(t);throw new Error("Invalid locale '"+r+"'")},isNumeric:function(t){return e(t),G.test(t)},isPort:function(e){return d(e,{min:0,max:65535})},isLowercase:function(t){return e(t),t===t.toLowerCase()},isUppercase:function(t){return e(t),t===t.toUpperCase()},isAscii:function(t){return e(t),z.test(t)},isFullWidth:function(t){return e(t),j.test(t)},isHalfWidth:function(t){return e(t),q.test(t)},isVariableWidth:function(t){return e(t),j.test(t)&&q.test(t)},isMultibyte:function(t){return e(t),W.test(t)},isSurrogatePair:function(t){return e(t),J.test(t)},isInt:d,isFloat:function(t,r){e(t),r=r||{};var o=new RegExp("^(?:[-+])?(?:[0-9]+)?(?:\\"+(r.locale?I[r.locale]:".")+"[0-9]*)?(?:[eE][\\+\\-]?(?:[0-9]+))?$");return""!==t&&"."!==t&&"-"!==t&&"+"!==t&&o.test(t)&&(!r.hasOwnProperty("min")||t>=r.min)&&(!r.hasOwnProperty("max")||t<=r.max)&&(!r.hasOwnProperty("lt")||tr.gt)},isDecimal:function(t,r){if(e(t),(r=i(r,V)).locale in I)return!Y.includes(t.replace(/ /g,""))&&function(e){return new RegExp("^[-+]?([0-9]+)?(\\"+I[e.locale]+"[0-9]{"+e.decimal_digits+"})"+(e.force_decimal?"":"?")+"$")}(r).test(t);throw new Error("Invalid locale '"+r.locale+"'")},isHexadecimal:c,isDivisibleBy:function(t,o){return e(t),r(t)%parseInt(o,10)==0},isHexColor:function(t){return e(t),X.test(t)},isISRC:function(t){return e(t),ee.test(t)},isMD5:function(t){return e(t),te.test(t)},isHash:function(t,r){return e(t),new RegExp("^[a-f0-9]{"+re[r]+"}$").test(t)},isJSON:function(t){e(t);try{var r=JSON.parse(t);return!!r&&"object"===(void 0===r?"undefined":_(r))}catch(e){}return!1},isEmpty:function(t){return e(t),0===t.length},isLength:function(t,r){e(t);var o=void 0,i=void 0;"object"===(void 0===r?"undefined":_(r))?(o=r.min||0,i=r.max):(o=arguments[1],i=arguments[2]);var n=t.match(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g)||[],a=t.length-n.length;return a>=o&&(void 0===i||a<=i)},isByteLength:n,isUUID:function(t){var r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"all";e(t);var o=oe[r];return o&&o.test(t)},isMongoId:function(t){return e(t),c(t)&&24===t.length},isAfter:function(r){var o=arguments.length>1&&void 0!==arguments[1]?arguments[1]:String(new Date);e(r);var i=t(o),n=t(r);return!!(n&&i&&n>i)},isBefore:function(r){var o=arguments.length>1&&void 0!==arguments[1]?arguments[1]:String(new Date);e(r);var i=t(o),n=t(r);return!!(n&&i&&n=0}return"object"===(void 0===r?"undefined":_(r))?r.hasOwnProperty(t):!(!r||"function"!=typeof r.indexOf)&&r.indexOf(t)>=0},isCreditCard:function(t){e(t);var r=t.replace(/[- ]+/g,"");if(!ie.test(r))return!1;for(var o=0,i=void 0,n=void 0,a=void 0,l=r.length-1;l>=0;l--)i=r.substring(l,l+1),n=parseInt(i,10),o+=a&&(n*=2)>=10?n%10+1:n,a=!a;return!(o%10!=0||!r)},isISIN:function(t){if(e(t),!ne.test(t))return!1;for(var r=t.replace(/[A-Z]/g,function(e){return parseInt(e,36)}),o=0,i=void 0,n=void 0,a=!0,l=r.length-2;l>=0;l--)i=r.substring(l,l+1),n=parseInt(i,10),o+=a&&(n*=2)>=10?n+1:n,a=!a;return parseInt(t.substr(t.length-1),10)===(1e4-o)%10},isISBN:f,isISSN:function(t){var r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};e(t);var o=ue;if(o=r.require_hyphen?o.replace("?",""):o,!(o=r.case_sensitive?new RegExp(o):new RegExp(o,"i")).test(t))return!1;var i=t.replace("-",""),n=8,a=0,l=!0,s=!1,u=void 0;try{for(var d,c=i[Symbol.iterator]();!(l=(d=c.next()).done);l=!0){var f=d.value;a+=("X"===f.toUpperCase()?10:+f)*n,--n}}catch(e){s=!0,u=e}finally{try{!l&&c.return&&c.return()}finally{if(s)throw u}}return a%11==0},isMobilePhone:function(t,r){if(e(t),r in de)return de[r].test(t);if("any"===r){for(var o in de)if(de.hasOwnProperty(o)&&de[o].test(t))return!0;return!1}throw new Error("Invalid locale '"+r+"'")},isPostalCode:function(t,r){if(e(t),r in Ae)return Ae[r].test(t);if("any"===r){for(var o in Ae)if(Ae.hasOwnProperty(o)&&Ae[o].test(t))return!0;return!1}throw new Error("Invalid locale '"+r+"'")},isCurrency:function(t,r){return e(t),r=i(r,ce),function(e){var t="\\d{"+e.digits_after_decimal[0]+"}";e.digits_after_decimal.forEach(function(e,r){0!==r&&(t=t+"|\\d{"+e+"}")});var r="(\\"+e.symbol.replace(/\./g,"\\.")+")"+(e.require_symbol?"":"?"),o="("+["0","[1-9]\\d*","[1-9]\\d{0,2}(\\"+e.thousands_separator+"\\d{3})*"].join("|")+")?",i="(\\"+e.decimal_separator+"("+t+"))"+(e.require_decimal?"":"?"),n=o+(e.allow_decimal||e.require_decimal?i:"");return e.allow_negatives&&!e.parens_for_negatives&&(e.negative_sign_after_digits?n+="-?":e.negative_sign_before_digits&&(n="-?"+n)),e.allow_negative_sign_placeholder?n="( (?!\\-))?"+n:e.allow_space_after_symbol?n=" ?"+n:e.allow_space_after_digits&&(n+="( (?!$))?"),e.symbol_after_digits?n+=r:n=r+n,e.allow_negatives&&(e.parens_for_negatives?n="(\\("+n+"\\)|"+n+")":e.negative_sign_before_digits||e.negative_sign_after_digits||(n="-?"+n)),new RegExp("^(?!-? )(?=.*\\d)"+n+"$")}(r).test(t)},isISO8601:function(t){return e(t),fe.test(t)},isISO31661Alpha2:function(t){return e(t),pe.includes(t.toUpperCase())},isBase64:function(t){e(t);var r=t.length;if(!r||r%4!=0||ge.test(t))return!1;var o=t.indexOf("=");return-1===o||o===r-1||o===r-2&&"="===t[r-1]},isDataURI:function(t){return e(t),he.test(t)},isLatLong:function(t){if(e(t),!t.includes(","))return!1;var r=t.split(",");return me.test(r[0])&&_e.test(r[1])},ltrim:p,rtrim:g,trim:function(e,t){return g(p(e,t),t)},escape:function(t){return e(t),t.replace(/&/g,"&").replace(/"/g,""").replace(/'/g,"'").replace(//g,">").replace(/\//g,"/").replace(/\\/g,"\").replace(/`/g,"`")},unescape:function(t){return e(t),t.replace(/&/g,"&").replace(/"/g,'"').replace(/'/g,"'").replace(/</g,"<").replace(/>/g,">").replace(///g,"/").replace(/\/g,"\\").replace(/`/g,"`")},stripLow:function(t,r){return e(t),h(t,r?"\\x00-\\x09\\x0B\\x0C\\x0E-\\x1F\\x7F":"\\x00-\\x1F\\x7F")},whitelist:function(t,r){return e(t),t.replace(new RegExp("[^"+r+"]+","g"),"")},blacklist:h,isWhitelisted:function(t,r){e(t);for(var o=t.length-1;o>=0;o--)if(-1===r.indexOf(t[o]))return!1;return!0},normalizeEmail:function(e,t){t=i(t,xe);var r=e.split("@"),o=r.pop(),n=[r.join("@"),o];if(n[1]=n[1].toLowerCase(),"gmail.com"===n[1]||"googlemail.com"===n[1]){if(t.gmail_remove_subaddress&&(n[0]=n[0].split("+")[0]),t.gmail_remove_dots&&(n[0]=n[0].replace(/\./g,"")),!n[0].length)return!1;(t.all_lowercase||t.gmail_lowercase)&&(n[0]=n[0].toLowerCase()),n[1]=t.gmail_convert_googlemaildotcom?"gmail.com":n[1]}else if(~Se.indexOf(n[1])){if(t.icloud_remove_subaddress&&(n[0]=n[0].split("+")[0]),!n[0].length)return!1;(t.all_lowercase||t.icloud_lowercase)&&(n[0]=n[0].toLowerCase())}else if(~we.indexOf(n[1])){if(t.outlookdotcom_remove_subaddress&&(n[0]=n[0].split("+")[0]),!n[0].length)return!1;(t.all_lowercase||t.outlookdotcom_lowercase)&&(n[0]=n[0].toLowerCase())}else if(~Ee.indexOf(n[1])){if(t.yahoo_remove_subaddress){var a=n[0].split("-");n[0]=a.length>1?a.slice(0,-1).join("-"):a[0]}if(!n[0].length)return!1;(t.all_lowercase||t.yahoo_lowercase)&&(n[0]=n[0].toLowerCase())}else t.all_lowercase&&(n[0]=n[0].toLowerCase());return n.join("@")},toString:o}});
\ No newline at end of file