Repository: validatorjs/validator.js
Pull request #2294 (2023-09-29 16:43:24)

feat(ulid): ULID validation
Added validation for ULID (Universally Unique Lexicographically Sortable Identifier).

Read more details on ULID in [github:ulid/spec](https://github.com/ulid/spec)
ULID Generator and validations on [https://ulid.page](https://ulid.page)

## Checklist

- [x] PR contains only changes related; no stray files, etc.
- [x] README updated (where applicable)
- [x] Tests written (where applicable)
- [x] References provided in PR (where applicable)


codecov[bot]: ## [Codecov](https://app.codecov.io/gh/validatorjs/validator.js/pull/2294?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=validatorjs) Report
All modified lines are covered by tests :white_check_mark:
> Comparison is base [(`b958bd7`)](https://app.codecov.io/gh/validatorjs/validator.js/commit/b958bd7d1026a434ad3bf90064d3dcb8b775f1a9?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=validatorjs) 99.95% compared to head [(`9891a3b`)](https://app.codecov.io/gh/validatorjs/validator.js/pull/2294?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=validatorjs) 99.95%.

<details><summary>Additional details and impacted files</summary>


```diff
@@           Coverage Diff           @@
##           master    #2294   +/-   ##
=======================================
  Coverage   99.95%   99.95%           
=======================================
  Files         107      108    +1     
  Lines        2454     2455    +1     
  Branches      619      620    +1     
=======================================
+ Hits         2453     2454    +1     
  Partials        1        1           
```


| [Files](https://app.codecov.io/gh/validatorjs/validator.js/pull/2294?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=validatorjs) | Coverage Δ | |
|---|---|---|
| [src/index.js](https://app.codecov.io/gh/validatorjs/validator.js/pull/2294?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=validatorjs#diff-c3JjL2luZGV4Lmpz) | `100.00% <100.00%> (ø)` | |
| [src/lib/isULID.js](https://app.codecov.io/gh/validatorjs/validator.js/pull/2294?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=validatorjs#diff-c3JjL2xpYi9pc1VMSUQuanM=) | `100.00% <100.00%> (ø)` | |

... and [2 files with indirect coverage changes](https://app.codecov.io/gh/validatorjs/validator.js/pull/2294/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=validatorjs)


</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/validatorjs/validator.js/pull/2294?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=validatorjs).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=validatorjs).

arafatkn: @profnandaa
I have fixed the conflicts.

Patch:
diff --git a/README.md b/README.md
index 873c01e..4c0d22c 100644
--- a/README.md
+++ b/README.md
@@ -167,6 +167,7 @@ Validator                               | Description
 **isTime(str [, options])**             | check if the string is a valid time e.g. [`23:01:59`, new Date().toLocaleTimeString()].<br/><br/> `options` is an object which can contain the keys `hourFormat` or `mode`.<br/><br/>`hourFormat` is a key and defaults to `'hour24'`.<br/><br/>`mode` is a key and defaults to `'default'`. <br/><br/>`hourFormat` can contain the values `'hour12'` or `'hour24'`, `'hour24'` will validate hours in 24 format and `'hour12'` will validate hours in 12 format. <br/><br/>`mode` can contain the values `'default'` or `'withSeconds'`, `'default'` will validate `HH:MM` format, `'withSeconds'` will validate the `HH:MM:SS` format.
 **isTaxID(str, locale)**                | check if the string is a valid Tax Identification Number. Default locale is `en-US`.<br/><br/>More info about exact TIN support can be found in `src/lib/isTaxID.js`.<br/><br/>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.<br/><br/>`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 }`.<br/><br/>`require_protocol` - if set to true isURL will return false if protocol is not present in the URL.<br/>`require_valid_protocol` - isURL will check if the URL's protocol is present in the protocols option.<br/>`protocols` - valid protocols can be modified with this option.<br/>`require_host` - if set to false isURL will not check if host is present in the URL.<br/>`require_port` - if set to true isURL will check if port is present in the URL.<br/>`allow_protocol_relative_urls` - if set to true protocol relative URLs will be allowed.<br/>`allow_fragments` - if set to false isURL will return false if fragments are present.<br/>`allow_query_components` - if set to false isURL will return false if query components are present.<br/>`validate_length` - if set to false isURL will skip string length validation (2083 characters is IE max 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.<br/>`version` is one of `'1'`-`'8'`, `'nil'`, `'max'`, or `'all'`.
 **isVariableWidth(str)**                | check if the string contains a mixture of full and half-width chars.
 **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]. <br/><br/>`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']`.
diff --git a/src/index.js b/src/index.js
index c1c4f03..1bc65a8 100644
--- a/src/index.js
+++ b/src/index.js
@@ -63,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';
 
@@ -186,6 +187,7 @@ const validator = {
   isLength,
   isLocale,
   isByteLength,
+  isULID,
   isUUID,
   isMongoId,
   isAfter,
diff --git a/src/lib/isULID.js b/src/lib/isULID.js
new file mode 100644
index 0000000..2ace6f4
--- /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);
+}


Test patch:
diff --git a/test/validators.test.js b/test/validators.test.js
index 1c1eb35..f9a2960 100644
--- a/test/validators.test.js
+++ b/test/validators.test.js
@@ -5420,6 +5420,29 @@ describe('Validators', () => {
     });
   });
 
+  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',