diff --git a/CHANGELOG.md b/CHANGELOG.md index c71a573..43da888 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,17 @@ ## Changelog -### [v5.20.0](https://github.com/panates/valgen/compare/v5.19.5...v5.20.0) - +### [v6.0.0](https://github.com/panates/valgen/compare/v5.20.0...v6.0.0) - + +#### 🛠 Refactoring and Updates + +- refactor: Improved typing exports using namespaces @Eray Hanoğlu +- refactor: Minor typing update @Eray Hanoğlu + +#### 💬 General Changes + +- dev: Fixed issues for new lint rules @Eray Hanoğlu + +### [v5.20.0](https://github.com/panates/valgen/compare/v5.19.5...v5.20.0) - 6 April 2026 #### 🚀 New Features diff --git a/package-lock.json b/package-lock.json index 7dfb5df..e12c7e2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "valgen", - "version": "5.20.0", + "version": "6.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "valgen", - "version": "5.20.0", + "version": "6.0.0", "license": "MIT", "dependencies": { "@browsery/validator": "^13.15.35", diff --git a/package.json b/package.json index fe97073..65a086b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "valgen", "description": "Fast runtime type validator, converter and io (encoding/decoding) library", - "version": "5.20.0", + "version": "6.0.0", "author": "Panates", "license": "MIT", "private": true, diff --git a/src/index.ts b/src/index.ts index b76d1d2..0644474 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,11 +1,8 @@ import { Context, ExecutionOptions } from './core/index.js'; -import { DatePrecision } from './rules/index.js'; import * as vg from './rules/index.js'; export * from './constants.js'; export * from './core/index.js'; -export type { DatePrecision } from './rules/type-rules/is-date.js'; -export type { IsObject } from './rules/type-rules/is-object.js'; const isAlpha = vg.isAlpha(); const isAlphanumeric = vg.isAlphanumeric(); @@ -69,18 +66,18 @@ const toDate = vg.isDate({ coerce: true }); const toDateStringValidators = new Map(); const toDateString = ( input: Date | string | number, - options?: ExecutionOptions & { trim?: DatePrecision }, + options?: ExecutionOptions & { trim?: vg.isDateString.Precision }, ctx?: Context, ) => { - const trim = options?.trim ?? 'ms'; - let validator = toDateStringValidators.get(trim); + const precisionMax = options?.trim ?? 'ms'; + let validator = toDateStringValidators.get(precisionMax); if (!validator) { validator = vg.isDateString({ coerce: true, - precisionMax: trim, + precisionMax, trim: true, }); - toDateStringValidators.set(trim, validator); + toDateStringValidators.set(precisionMax, validator); } return validator(input, options, ctx); }; diff --git a/src/rules/format-rules/is-alpha.ts b/src/rules/format-rules/is-alpha.ts index 6d26838..a6b3c03 100644 --- a/src/rules/format-rules/is-alpha.ts +++ b/src/rules/format-rules/is-alpha.ts @@ -10,9 +10,9 @@ import { * Check if the string contains only letters (a-zA-Z). * @validator isAlpha */ -export function isAlpha(options?: ValidationOptions) { +export function isAlpha(options?: isAlpha.Options) { return validator( - 'isLowercase', + 'isAlpha', (input: unknown, context: Context, _this): Nullish => { if (typeof input === 'string' && validatorJS.isAlpha(input)) return input; context.fail(_this, `"Value must be an alpha string`, input); @@ -21,19 +21,6 @@ export function isAlpha(options?: ValidationOptions) { ); } -/** - * Check if the string contains only letters and numbers. - * @validator isAlphanumeric - */ -export function isAlphanumeric(options?: ValidationOptions) { - return validator( - 'isAlphanumeric', - (input: unknown, context: Context, _this): Nullish => { - if (typeof input === 'string' && validatorJS.isAlphanumeric(input)) { - return input; - } - context.fail(_this, `"Value must be an alphanumeric string`, input); - }, - options, - ); +export namespace isAlpha { + export interface Options extends ValidationOptions {} } diff --git a/src/rules/format-rules/is-alphanumeric.ts b/src/rules/format-rules/is-alphanumeric.ts new file mode 100644 index 0000000..45c95e4 --- /dev/null +++ b/src/rules/format-rules/is-alphanumeric.ts @@ -0,0 +1,28 @@ +import validatorJS from '@browsery/validator'; +import type { Nullish } from 'ts-gems'; +import { + type Context, + type ValidationOptions, + validator, +} from '../../core/index.js'; + +/** + * Check if the string contains only letters and numbers. + * @validator isAlphanumeric + */ +export function isAlphanumeric(options?: isAlphanumeric.Options) { + return validator( + 'isAlphanumeric', + (input: unknown, context: Context, _this): Nullish => { + if (typeof input === 'string' && validatorJS.isAlphanumeric(input)) { + return input; + } + context.fail(_this, `"Value must be an alphanumeric string`, input); + }, + options, + ); +} + +export namespace isAlphanumeric { + export interface Options extends ValidationOptions {} +} diff --git a/src/rules/format-rules/is-ascii.ts b/src/rules/format-rules/is-ascii.ts index 21305ae..290503b 100644 --- a/src/rules/format-rules/is-ascii.ts +++ b/src/rules/format-rules/is-ascii.ts @@ -10,7 +10,7 @@ import { * Check if the string contains ASCII chars only. * @validator isAscii */ -export function isAscii(options?: ValidationOptions) { +export function isAscii(options?: isAscii.Options) { return validator( 'isAscii', (input: unknown, context: Context, _this): Nullish => { @@ -20,3 +20,7 @@ export function isAscii(options?: ValidationOptions) { options, ); } + +export namespace isAscii { + export interface Options extends ValidationOptions {} +} diff --git a/src/rules/format-rules/is-base64.ts b/src/rules/format-rules/is-base64.ts index 018e8dc..fcb3185 100644 --- a/src/rules/format-rules/is-base64.ts +++ b/src/rules/format-rules/is-base64.ts @@ -8,14 +8,11 @@ import { validator, } from '../../core/index.js'; -export interface Base64ValidatorOptions - extends ValidationOptions, _IsBase64Options {} - /** * Validates if value is a "Base64" string. * @validator isBase64 */ -export function isBase64(options?: Base64ValidatorOptions) { +export function isBase64(options?: isBase64.Options) { return validator( 'isBase64', (input: unknown, context: Context, _this): Nullish => { @@ -27,3 +24,7 @@ export function isBase64(options?: Base64ValidatorOptions) { options, ); } + +export namespace isBase64 { + export interface Options extends ValidationOptions, _IsBase64Options {} +} diff --git a/src/rules/format-rules/is-btc-address.ts b/src/rules/format-rules/is-btc-address.ts index 5aa91d2..7407470 100644 --- a/src/rules/format-rules/is-btc-address.ts +++ b/src/rules/format-rules/is-btc-address.ts @@ -10,7 +10,7 @@ import { * Validates if value is a BTC address. * @validator isBtcAddress */ -export function isBtcAddress(options?: ValidationOptions) { +export function isBtcAddress(options?: isBtcAddress.Options) { return validator( 'isBtcAddress', (input: unknown, context: Context, _this): Nullish => { @@ -22,3 +22,7 @@ export function isBtcAddress(options?: ValidationOptions) { options, ); } + +export namespace isBtcAddress { + export interface Options extends ValidationOptions {} +} diff --git a/src/rules/format-rules/is-credit-card.ts b/src/rules/format-rules/is-credit-card.ts index c26658a..b979d8a 100644 --- a/src/rules/format-rules/is-credit-card.ts +++ b/src/rules/format-rules/is-credit-card.ts @@ -8,14 +8,11 @@ import { validator, } from '../../core/index.js'; -export interface CreditCardValidatorOptions - extends ValidationOptions, _IsCreditCardOptions {} - /** * Validates if value is a credit card number * @validator isCreditCard */ -export function isCreditCard(options?: CreditCardValidatorOptions) { +export function isCreditCard(options?: isCreditCard.Options) { return validator( 'isCreditCard', (input: unknown, context: Context, _this): Nullish => { @@ -30,3 +27,7 @@ export function isCreditCard(options?: CreditCardValidatorOptions) { options, ); } + +export namespace isCreditCard { + export interface Options extends ValidationOptions, _IsCreditCardOptions {} +} diff --git a/src/rules/format-rules/is-decimal.ts b/src/rules/format-rules/is-decimal.ts index e0a8529..9998a8c 100644 --- a/src/rules/format-rules/is-decimal.ts +++ b/src/rules/format-rules/is-decimal.ts @@ -11,7 +11,7 @@ import { * such as `0.1`, `.3`, `1.1`, `1.00003`, `4.0` etc. * @validator isDecimal */ -export function isDecimal(options?: ValidationOptions) { +export function isDecimal(options?: isDecimal.Options) { return validator( 'isDecimal', (input: unknown, context: Context, _this): Nullish => { @@ -23,3 +23,7 @@ export function isDecimal(options?: ValidationOptions) { options, ); } + +export namespace isDecimal { + export interface Options extends ValidationOptions {} +} diff --git a/src/rules/format-rules/is-ean.ts b/src/rules/format-rules/is-ean.ts index 9db1707..2a7d228 100644 --- a/src/rules/format-rules/is-ean.ts +++ b/src/rules/format-rules/is-ean.ts @@ -10,7 +10,7 @@ import { * Validates if value is an EAN (European Article Number) * @validator isEAN */ -export function isEAN(options?: ValidationOptions) { +export function isEAN(options?: isEAN.Options) { return validator( 'isEAN', (input: unknown, context: Context, _this): Nullish => { @@ -24,3 +24,7 @@ export function isEAN(options?: ValidationOptions) { options, ); } + +export namespace isEAN { + export interface Options extends ValidationOptions {} +} diff --git a/src/rules/format-rules/is-email.ts b/src/rules/format-rules/is-email.ts index 0bd7ab8..5ff296e 100644 --- a/src/rules/format-rules/is-email.ts +++ b/src/rules/format-rules/is-email.ts @@ -8,76 +8,11 @@ import { validator, } from '../../core/index.js'; -export interface IsEmailOptions extends ValidationOptions { - /** - * If set to `true`, the validator will also match `Display Name `. - * - * @default false - */ - allowDisplayName?: boolean; - - /** - * If set to `true`, the validator will reject strings without the format `Display Name `. - * - * @default false - */ - requireDisplayName?: boolean; - - /** - * If set to `false`, the validator will not allow any non-English UTF8 character in email address' local part. - * - * @default true - */ - utf8LocalPart?: boolean; - - /** - * If set to `true`, the validator will not check for the standard max length of an email. - * - * @default false - */ - ignoreMaxLength?: boolean; - - /** - * If set to `true`, the validator will allow IP addresses in the host part. - * - * @default false - */ - allowIpDomain?: boolean; - - /** - * If set to `true`, some additional validation will be enabled, - * e.g. disallowing certain syntactically valid email addresses that are rejected by GMail. - * - * @default false - */ - domainSpecificValidation?: boolean; - - /** - * If set to an array of strings and the part of the email after - * the @ symbol matches one of the strings defined in it, - * the validation fails. - */ - hostBlacklist?: string[]; - - /** - * If set to an array of strings and the part of the email after - * the @ symbol matches none of the strings defined in it, - * the validation fails. - */ - hostWhitelist?: string[]; - - /** - * If set to a string, then the validator will reject emails that include - * any of the characters in the string, in the name part. - */ - blacklistedChars?: string; -} - /** * Validates if value is a valid Email * @validator isEmail */ -export function isEmail(options?: IsEmailOptions) { +export function isEmail(options?: isEmail.Options) { const emailOptions: _IsEmailOptions = { allow_display_name: true, allow_utf8_local_part: options?.utf8LocalPart, @@ -156,3 +91,70 @@ export function isEmail(options?: IsEmailOptions) { options, ); } + +export namespace isEmail { + export interface Options extends ValidationOptions { + /** + * If set to `true`, the validator will also match `Display Name `. + * + * @default false + */ + allowDisplayName?: boolean; + + /** + * If set to `true`, the validator will reject strings without the format `Display Name `. + * + * @default false + */ + requireDisplayName?: boolean; + + /** + * If set to `false`, the validator will not allow any non-English UTF8 character in email address' local part. + * + * @default true + */ + utf8LocalPart?: boolean; + + /** + * If set to `true`, the validator will not check for the standard max length of an email. + * + * @default false + */ + ignoreMaxLength?: boolean; + + /** + * If set to `true`, the validator will allow IP addresses in the host part. + * + * @default false + */ + allowIpDomain?: boolean; + + /** + * If set to `true`, some additional validation will be enabled, + * e.g. disallowing certain syntactically valid email addresses that are rejected by GMail. + * + * @default false + */ + domainSpecificValidation?: boolean; + + /** + * If set to an array of strings and the part of the email after + * the @ symbol matches one of the strings defined in it, + * the validation fails. + */ + hostBlacklist?: string[]; + + /** + * If set to an array of strings and the part of the email after + * the @ symbol matches none of the strings defined in it, + * the validation fails. + */ + hostWhitelist?: string[]; + + /** + * If set to a string, then the validator will reject emails that include + * any of the characters in the string, in the name part. + */ + blacklistedChars?: string; + } +} diff --git a/src/rules/format-rules/is-eth-address.ts b/src/rules/format-rules/is-eth-address.ts index 4d6814f..61f7dd3 100644 --- a/src/rules/format-rules/is-eth-address.ts +++ b/src/rules/format-rules/is-eth-address.ts @@ -7,10 +7,10 @@ import { } from '../../core/index.js'; /** - * Validates if value is a ETH (Ethereum) address. + * Validates if value is an ETH (Ethereum) address. * @validator isETHAddress */ -export function isETHAddress(options?: ValidationOptions) { +export function isETHAddress(options?: isETHAddress.Options) { return validator( 'isETHAddress', (input: unknown, context: Context, _this): Nullish => { @@ -22,3 +22,7 @@ export function isETHAddress(options?: ValidationOptions) { options, ); } + +export namespace isETHAddress { + export interface Options extends ValidationOptions {} +} diff --git a/src/rules/format-rules/is-fqdn.ts b/src/rules/format-rules/is-fqdn.ts index 2f8635d..5b1c6ec 100644 --- a/src/rules/format-rules/is-fqdn.ts +++ b/src/rules/format-rules/is-fqdn.ts @@ -8,19 +8,11 @@ import { validator, } from '../../core/index.js'; -export interface IsFQDNOptions extends ValidationOptions { - /** - * If set to true, the validator will allow domain starting with `*.` (e.g. `*.example.com` or `*.shop.example.com`). - * @default false - */ - allowWildcard?: boolean; -} - /** * Validates if value is an FQDN * @validator isFQDN */ -export function isFQDN(options?: IsFQDNOptions) { +export function isFQDN(options?: isFQDN.Options) { const opts: _IsFQDNOptions = { allow_wildcard: options?.allowWildcard, }; @@ -35,3 +27,13 @@ export function isFQDN(options?: IsFQDNOptions) { options, ); } + +export namespace isFQDN { + export interface Options extends ValidationOptions { + /** + * If set to true, the validator will allow domain starting with `*.` (e.g. `*.example.com` or `*.shop.example.com`). + * @default false + */ + allowWildcard?: boolean; + } +} diff --git a/src/rules/format-rules/is-hash.ts b/src/rules/format-rules/is-hash.ts index f5e911d..6ab4fbe 100644 --- a/src/rules/format-rules/is-hash.ts +++ b/src/rules/format-rules/is-hash.ts @@ -8,13 +8,14 @@ import { validator, } from '../../core/index.js'; -export type HashAlgorithm = _HashAlgorithm; - /** * Validates if value a hash of type algorithm * @validator isHash */ -export function isHash(algorithm: HashAlgorithm, options?: ValidationOptions) { +export function isHash( + algorithm: isHash.HashAlgorithm, + options?: ValidationOptions, +) { return validator( 'isHash', (input: unknown, context: Context, _this): Nullish => { @@ -26,3 +27,7 @@ export function isHash(algorithm: HashAlgorithm, options?: ValidationOptions) { options, ); } + +export namespace isHash { + export type HashAlgorithm = _HashAlgorithm; +} diff --git a/src/rules/format-rules/is-hex-color.ts b/src/rules/format-rules/is-hex-color.ts index 61960e3..dcb8bbe 100644 --- a/src/rules/format-rules/is-hex-color.ts +++ b/src/rules/format-rules/is-hex-color.ts @@ -10,7 +10,7 @@ import { * Validates if value is a Hex Color * @validator isHexColor */ -export function isHexColor(options?: ValidationOptions) { +export function isHexColor(options?: isHexColor.Options) { return validator( 'isHexColor', (input: unknown, context: Context, _this): Nullish => { @@ -22,3 +22,7 @@ export function isHexColor(options?: ValidationOptions) { options, ); } + +export namespace isHexColor { + export interface Options extends ValidationOptions {} +} diff --git a/src/rules/format-rules/is-hex.ts b/src/rules/format-rules/is-hex.ts index 187f447..0b9ccad 100644 --- a/src/rules/format-rules/is-hex.ts +++ b/src/rules/format-rules/is-hex.ts @@ -10,7 +10,7 @@ import { * Check if the string is a hexadecimal number. * @validator isHex */ -export function isHex(options?: ValidationOptions) { +export function isHex(options?: isHex.Options) { return validator( 'isHex', (input: unknown, context: Context, _this): Nullish => { @@ -22,3 +22,7 @@ export function isHex(options?: ValidationOptions) { options, ); } + +export namespace isHex { + export interface Options extends ValidationOptions {} +} diff --git a/src/rules/format-rules/is-iban.ts b/src/rules/format-rules/is-iban.ts index 9271f8a..45cc020 100644 --- a/src/rules/format-rules/is-iban.ts +++ b/src/rules/format-rules/is-iban.ts @@ -10,7 +10,7 @@ import { * Validates if value is an IBAN (International Bank Account Number) * @validator isIBAN */ -export function isIBAN(options?: ValidationOptions) { +export function isIBAN(options?: isIBAN.Options) { return validator( 'isIBAN', (input: unknown, context: Context, _this): Nullish => { @@ -25,21 +25,6 @@ export function isIBAN(options?: ValidationOptions) { ); } -/** - * Validates if value is a BIC (Bank Identification Code) or SWIFT code - * @validator isSWIFT - */ -export function isSWIFT(options?: ValidationOptions) { - return validator( - 'isSWIFT', - (input: unknown, context: Context, _this): Nullish => { - if (typeof input === 'string' && validatorJS.isBIC(input)) return input; - context.fail( - _this, - `Value must be a valid a BIC (Bank Identification Code) or SWIFT code`, - input, - ); - }, - options, - ); +export namespace isIBAN { + export interface Options extends ValidationOptions {} } diff --git a/src/rules/format-rules/is-ip.ts b/src/rules/format-rules/is-ip.ts index 1f1c8a6..f362e32 100644 --- a/src/rules/format-rules/is-ip.ts +++ b/src/rules/format-rules/is-ip.ts @@ -1,4 +1,4 @@ -import validatorJS from '@browsery/validator'; +import validatorJS, { type IPVersion as _IPVersion } from '@browsery/validator'; import { type Nullish } from 'ts-gems'; import { type Context, @@ -10,7 +10,7 @@ import { * Validates if value is an IP * @validator isIP */ -export function isIP(version?: 4 | 6, options?: ValidationOptions) { +export function isIP(version?: isIP.IPVersion, options?: ValidationOptions) { return validator( 'isIP', (input: unknown, context: Context, _this): Nullish => { @@ -35,7 +35,10 @@ export function isIP(version?: 4 | 6, options?: ValidationOptions) { * Validates if value is an IP * @validator isIPRange */ -export function isIPRange(version?: 4 | 6, options?: ValidationOptions) { +export function isIPRange( + version?: isIPRange.IPVersion, + options?: ValidationOptions, +) { return validator( 'isIPRange', (input: unknown, context: Context, _this): Nullish => { @@ -55,3 +58,11 @@ export function isIPRange(version?: 4 | 6, options?: ValidationOptions) { options, ); } + +export namespace isIP { + export type IPVersion = _IPVersion; +} + +export namespace isIPRange { + export type IPVersion = _IPVersion; +} diff --git a/src/rules/format-rules/is-issn.ts b/src/rules/format-rules/is-issn.ts index a76c088..e6bbac2 100644 --- a/src/rules/format-rules/is-issn.ts +++ b/src/rules/format-rules/is-issn.ts @@ -8,20 +8,11 @@ import { validator, } from '../../core/index.js'; -export interface IsISSNOptions extends ValidationOptions { - /** - * If set to `true`, ISSNs with a lowercase `x` as the check digit are rejected. - * - * @default false - */ - caseSensitive?: boolean; -} - /** * Validates if value is an ISSN * @validator isISSN */ -export function isISSN(options?: IsISSNOptions) { +export function isISSN(options?: isISSN.Options) { const opts: _IsISSNOptions = { case_sensitive: options?.caseSensitive, }; @@ -36,3 +27,14 @@ export function isISSN(options?: IsISSNOptions) { options, ); } + +export namespace isISSN { + export interface Options extends ValidationOptions { + /** + * If set to `true`, ISSNs with a lowercase `x` as the check digit are rejected. + * + * @default false + */ + caseSensitive?: boolean; + } +} diff --git a/src/rules/format-rules/is-jwt.ts b/src/rules/format-rules/is-jwt.ts index df0d7d7..81251e3 100644 --- a/src/rules/format-rules/is-jwt.ts +++ b/src/rules/format-rules/is-jwt.ts @@ -10,7 +10,7 @@ import { * Validates if value a valid JWT token * @validator isJWT */ -export function isJWT(options?: ValidationOptions) { +export function isJWT(options?: isJWT.Options) { return validator( 'isJWT', (input: unknown, context: Context, _this): Nullish => { @@ -20,3 +20,7 @@ export function isJWT(options?: ValidationOptions) { options, ); } + +export namespace isJWT { + export interface Options extends ValidationOptions {} +} diff --git a/src/rules/format-rules/is-lowercase.ts b/src/rules/format-rules/is-lowercase.ts index 93c1ee2..6669f5f 100644 --- a/src/rules/format-rules/is-lowercase.ts +++ b/src/rules/format-rules/is-lowercase.ts @@ -10,7 +10,7 @@ import { * Validates if value a Lowercase string * @validator isLowercase */ -export function isLowercase(options?: ValidationOptions) { +export function isLowercase(options?: isLowercase.Options) { return validator( 'isLowercase', (input: unknown, context: Context, _this): Nullish => { @@ -23,19 +23,6 @@ export function isLowercase(options?: ValidationOptions) { ); } -/** - * Validates if value a Uppercase string - * @validator isUppercase - */ -export function isUppercase(options?: ValidationOptions) { - return validator( - 'isUppercase', - (input: unknown, context: Context, _this): Nullish => { - if (typeof input === 'string' && validatorJS.isUppercase(input)) { - return input; - } - context.fail(_this, `Value must be an uppercase string`, input); - }, - options, - ); +export namespace isLowercase { + export interface Options extends ValidationOptions {} } diff --git a/src/rules/format-rules/is-mac-address.ts b/src/rules/format-rules/is-mac-address.ts index e3be164..6345e3c 100644 --- a/src/rules/format-rules/is-mac-address.ts +++ b/src/rules/format-rules/is-mac-address.ts @@ -8,28 +8,11 @@ import { validator, } from '../../core/index.js'; -export interface IsMACAddressOptions extends ValidationOptions { - /** - * If set to `true`, the validator will allow MAC addresses without the colons. - * Also, it allows the use of hyphens or spaces. - * - * e.g. `01 02 03 04 05 ab` or `01-02-03-04-05-ab`. - * - * @default false - */ - noSeparators?: boolean; - - /** - * Setting `eui` allows for validation against EUI-48 or EUI-64 instead of both. - */ - eui?: '48' | '64'; -} - /** * Validates if value is an MACAddress * @validator isMACAddress */ -export function isMACAddress(options?: IsMACAddressOptions) { +export function isMACAddress(options?: isMACAddress.Options) { const opts: _IsMACAddressOptions = { no_separators: options?.noSeparators, eui: options?.eui, @@ -49,3 +32,22 @@ export function isMACAddress(options?: IsMACAddressOptions) { options, ); } + +export namespace isMACAddress { + export interface Options extends ValidationOptions { + /** + * If set to `true`, the validator will allow MAC addresses without the colons. + * Also, it allows the use of hyphens or spaces. + * + * e.g. `01 02 03 04 05 ab` or `01-02-03-04-05-ab`. + * + * @default false + */ + noSeparators?: boolean; + + /** + * Setting `eui` allows for validation against EUI-48 or EUI-64 instead of both. + */ + eui?: '48' | '64'; + } +} diff --git a/src/rules/format-rules/is-mobile-phone.ts b/src/rules/format-rules/is-mobile-phone.ts index a193fcb..28590d7 100644 --- a/src/rules/format-rules/is-mobile-phone.ts +++ b/src/rules/format-rules/is-mobile-phone.ts @@ -9,26 +9,11 @@ import { validator, } from '../../core/index.js'; -export interface IsMobilePhoneOptions extends ValidationOptions { - /** - * If this is set to `true`, the mobile phone number must be supplied with the country code and therefore must start with `+`. - * - * @default false - */ - strictMode?: boolean; - - /** - * Locale or locales of the mobile phone - * @default 'any' - */ - locale?: 'any' | _MobilePhoneLocale | _MobilePhoneLocale[]; -} - /** * Validates if value is a valid Email * @validator isMobilePhone */ -export function isMobilePhone(options?: IsMobilePhoneOptions) { +export function isMobilePhone(options?: isMobilePhone.Options) { const opts: _IsMobilePhoneOptions = { strictMode: options?.strictMode, }; @@ -46,3 +31,20 @@ export function isMobilePhone(options?: IsMobilePhoneOptions) { options, ); } + +export namespace isMobilePhone { + export interface Options extends ValidationOptions { + /** + * If this is set to `true`, the mobile phone number must be supplied with the country code and therefore must start with `+`. + * + * @default false + */ + strictMode?: boolean; + + /** + * Locale or locales of the mobile phone + * @default 'any' + */ + locale?: 'any' | _MobilePhoneLocale | _MobilePhoneLocale[]; + } +} diff --git a/src/rules/format-rules/is-object-id.ts b/src/rules/format-rules/is-object-id.ts index cf862e4..08f7833 100644 --- a/src/rules/format-rules/is-object-id.ts +++ b/src/rules/format-rules/is-object-id.ts @@ -6,24 +6,18 @@ import { validator, } from '../../core/index.js'; -export declare interface ObjectIdLike { - id: string | Uint8Array; - - toHexString(): string; -} - /** * Validates if value is an "ObjectId". * @validator isObjectId */ export function isObjectId(options?: ValidationOptions) { - return validator( + return validator( 'isObjectId', ( input: any, context: Context, _this, - ): Nullish => { + ): Nullish => { if ( input != null && (_isObjectIdValue(input) || @@ -47,3 +41,11 @@ function _isObjectIdValue(input: string | Uint8Array): boolean { validatorJS.isHexadecimal(input)) ); } + +export namespace isObjectId { + export declare interface ObjectIdLike { + id: string | Uint8Array; + + toHexString(): string; + } +} diff --git a/src/rules/format-rules/is-passport-number.ts b/src/rules/format-rules/is-passport-number.ts index 263f13b..7d59e4c 100644 --- a/src/rules/format-rules/is-passport-number.ts +++ b/src/rules/format-rules/is-passport-number.ts @@ -12,7 +12,7 @@ import { */ export function isPassportNumber( countryCode: string, - options?: ValidationOptions, + options?: isPassportNumber.Options, ) { return validator( 'isPassportNumber', @@ -32,3 +32,7 @@ export function isPassportNumber( options, ); } + +export namespace isPassportNumber { + export interface Options extends ValidationOptions {} +} diff --git a/src/rules/format-rules/is-port.ts b/src/rules/format-rules/is-port.ts index a0537f7..fb2d06b 100644 --- a/src/rules/format-rules/is-port.ts +++ b/src/rules/format-rules/is-port.ts @@ -10,7 +10,7 @@ import { * Validates if value is a port number * @validator isPort */ -export function isPort(options?: ValidationOptions) { +export function isPort(options?: isPort.Options) { return validator( 'isPort', (input: unknown, context: Context, _this): Nullish => { @@ -27,3 +27,7 @@ export function isPort(options?: ValidationOptions) { options, ); } + +export namespace isPort { + export interface Options extends ValidationOptions {} +} diff --git a/src/rules/format-rules/is-swift.ts b/src/rules/format-rules/is-swift.ts new file mode 100644 index 0000000..4be7731 --- /dev/null +++ b/src/rules/format-rules/is-swift.ts @@ -0,0 +1,30 @@ +import validatorJS from '@browsery/validator'; +import type { Nullish } from 'ts-gems'; +import { + type Context, + type ValidationOptions, + validator, +} from '../../core/index.js'; + +/** + * Validates if value is a BIC (Bank Identification Code) or SWIFT code + * @validator isSWIFT + */ +export function isSWIFT(options?: isSWIFT.Options) { + return validator( + 'isSWIFT', + (input: unknown, context: Context, _this): Nullish => { + if (typeof input === 'string' && validatorJS.isBIC(input)) return input; + context.fail( + _this, + `Value must be a valid a BIC (Bank Identification Code) or SWIFT code`, + input, + ); + }, + options, + ); +} + +export namespace isSWIFT { + export interface Options extends ValidationOptions {} +} diff --git a/src/rules/format-rules/is-time.ts b/src/rules/format-rules/is-time.ts index 33ca761..9c68152 100644 --- a/src/rules/format-rules/is-time.ts +++ b/src/rules/format-rules/is-time.ts @@ -5,15 +5,13 @@ import { validator, } from '../../core/index.js'; -export interface IsTimeOptions extends ValidationOptions {} - const TIME_PATTERN = /^(\d{2}):?(\d{2})(?::?(\d{2})?(?:\.(\d{1,3}))?)?$/; /** * Validates if value is a time formatted string * @validator isTime */ -export function isTime(options?: IsTimeOptions) { +export function isTime(options?: isTime.Options) { return validator( 'isTime', (input: unknown, context: Context, _this): Nullish => { @@ -55,3 +53,7 @@ export function isTime(options?: IsTimeOptions) { options, ); } + +export namespace isTime { + export interface Options extends ValidationOptions {} +} diff --git a/src/rules/format-rules/is-uppercase.ts b/src/rules/format-rules/is-uppercase.ts new file mode 100644 index 0000000..20a7258 --- /dev/null +++ b/src/rules/format-rules/is-uppercase.ts @@ -0,0 +1,28 @@ +import validatorJS from '@browsery/validator'; +import type { Nullish } from 'ts-gems'; +import { + type Context, + type ValidationOptions, + validator, +} from '../../core/index.js'; + +/** + * Validates if value a Uppercase string + * @validator isUppercase + */ +export function isUppercase(options?: isUppercase.Options) { + return validator( + 'isUppercase', + (input: unknown, context: Context, _this): Nullish => { + if (typeof input === 'string' && validatorJS.isUppercase(input)) { + return input; + } + context.fail(_this, `Value must be an uppercase string`, input); + }, + options, + ); +} + +export namespace isUppercase { + export interface Options extends ValidationOptions {} +} diff --git a/src/rules/format-rules/is-url.ts b/src/rules/format-rules/is-url.ts index c322cce..e9a9d1c 100644 --- a/src/rules/format-rules/is-url.ts +++ b/src/rules/format-rules/is-url.ts @@ -8,13 +8,11 @@ import { validator, } from '../../core/index.js'; -export interface IsURLOptions extends ValidationOptions, _IsURLOptions {} - /** * Validates if value is an URL * @validator isURL */ -export function isURL(options?: IsURLOptions) { +export function isURL(options?: isURL.Options) { return validator( 'isURL', (input: unknown, context: Context, _this): Nullish => { @@ -30,3 +28,7 @@ export function isURL(options?: IsURLOptions) { options, ); } + +export namespace isURL { + export interface Options extends ValidationOptions, _IsURLOptions {} +} diff --git a/src/rules/format-rules/is-uuid.ts b/src/rules/format-rules/is-uuid.ts index b991a39..e9cdd22 100644 --- a/src/rules/format-rules/is-uuid.ts +++ b/src/rules/format-rules/is-uuid.ts @@ -8,13 +8,11 @@ import { validator, } from '../../core/index.js'; -export type UUIDVersion = _UUIDVersion; - /** * Validates if value is a "UUID". * @validator isUUID */ -export function isUUID(version?: UUIDVersion, options?: ValidationOptions) { +export function isUUID(version?: isUUID.UUIDVersion, options?: isUUID.Options) { return validator( 'isUUID', (input: unknown, context: Context, _this): Nullish => { @@ -34,3 +32,8 @@ export function isUUID(version?: UUIDVersion, options?: ValidationOptions) { options, ); } + +export namespace isUUID { + export type UUIDVersion = _UUIDVersion; + export interface Options extends ValidationOptions {} +} diff --git a/src/rules/format-rules/is-vat-number.ts b/src/rules/format-rules/is-vat-number.ts index bfb4ea9..6bd490c 100644 --- a/src/rules/format-rules/is-vat-number.ts +++ b/src/rules/format-rules/is-vat-number.ts @@ -8,15 +8,13 @@ import { validator, } from '../../core/index.js'; -export type VATCountryCode = _VATCountryCode; - /** * Validates if value is a VAT number * @validator isVAT */ export function isVATNumber( - countryCode: VATCountryCode, - options?: ValidationOptions, + countryCode: isVATNumber.CountryCode, + options?: isVATNumber.Options, ) { return validator( 'isVATNumber', @@ -29,3 +27,8 @@ export function isVATNumber( options, ); } + +export namespace isVATNumber { + export type CountryCode = _VATCountryCode; + export interface Options extends ValidationOptions {} +} diff --git a/src/rules/format-rules/matches.ts b/src/rules/format-rules/matches.ts index b7f9cdc..9966adc 100644 --- a/src/rules/format-rules/matches.ts +++ b/src/rules/format-rules/matches.ts @@ -5,15 +5,11 @@ import { validator, } from '../../core/index.js'; -export interface IsRegExpOptions extends ValidationOptions { - formatName?: string; -} - /** * Coerces given value to "UUID" format or returns undefined if nullish * @validator uuid */ -export function matches(format: string | RegExp, options?: IsRegExpOptions) { +export function matches(format: string | RegExp, options?: matches.Options) { const regExp = format instanceof RegExp ? format : new RegExp(format); const formatName = options?.formatName; return validator( @@ -34,3 +30,9 @@ export function matches(format: string | RegExp, options?: IsRegExpOptions) { options, ); } + +export namespace matches { + export interface Options extends ValidationOptions { + formatName?: string; + } +} diff --git a/src/rules/index.ts b/src/rules/index.ts index 21d08f0..26fd208 100644 --- a/src/rules/index.ts +++ b/src/rules/index.ts @@ -1,4 +1,5 @@ export * from './format-rules/is-alpha.js'; +export * from './format-rules/is-alphanumeric.js'; export * from './format-rules/is-ascii.js'; export * from './format-rules/is-base64.js'; export * from './format-rules/is-btc-address.js'; @@ -21,7 +22,9 @@ export * from './format-rules/is-mobile-phone.js'; export * from './format-rules/is-object-id.js'; export * from './format-rules/is-passport-number.js'; export * from './format-rules/is-port.js'; +export * from './format-rules/is-swift.js'; export * from './format-rules/is-time.js'; +export * from './format-rules/is-uppercase.js'; export * from './format-rules/is-url.js'; export * from './format-rules/is-uuid.js'; export * from './format-rules/is-vat-number.js'; @@ -29,6 +32,11 @@ export * from './format-rules/matches.js'; export * from './logical-rules/is-defined.js'; export * from './logical-rules/is-empty.js'; export * from './logical-rules/is-equal.js'; +export * from './logical-rules/is-gt.js'; +export * from './logical-rules/is-gte.js'; +export * from './logical-rules/is-lt.js'; +export * from './logical-rules/is-lte.js'; +export * from './logical-rules/length.js'; export * from './logical-rules/range.js'; export * from './type-rules/is-any.js'; export * from './type-rules/is-array.js'; @@ -51,3 +59,4 @@ export * from './utility-rules/one-of.js'; export * from './utility-rules/optional.js'; export * from './utility-rules/pipe.js'; export * from './utility-rules/required.js'; +export * from './utility-rules/string-utils.js'; diff --git a/src/rules/logical-rules/is-defined.ts b/src/rules/logical-rules/is-defined.ts index b550c6f..7e4e839 100644 --- a/src/rules/logical-rules/is-defined.ts +++ b/src/rules/logical-rules/is-defined.ts @@ -8,7 +8,7 @@ import { * Validates if value is not "undefined" nor "null" * @validator isDefined */ -export function isDefined(options?: ValidationOptions) { +export function isDefined(options?: isDefined.Options) { return validator( 'is-defined', (input: unknown, context: Context, _this) => { @@ -18,3 +18,7 @@ export function isDefined(options?: ValidationOptions) { options, ); } + +export namespace isDefined { + export interface Options extends ValidationOptions {} +} diff --git a/src/rules/logical-rules/is-empty.ts b/src/rules/logical-rules/is-empty.ts index b4e6fa9..bbcab92 100644 --- a/src/rules/logical-rules/is-empty.ts +++ b/src/rules/logical-rules/is-empty.ts @@ -4,14 +4,12 @@ import { validator, } from '../../core/index.js'; -type IsEmptyInput = any; - /** - * Checks if value is an empty. Value should be string, array, set, map or object + * Checks if the value is empty. Value should be string, array, set, map or object * @validator isEmpty */ -export function isEmpty(options?: ValidationOptions) { - return validator( +export function isEmpty(options?: isEmpty.Options) { + return validator( 'isEmpty', (input: unknown, context: Context, _this) => { if (input == null) return input; @@ -50,12 +48,16 @@ export function isEmpty(options?: ValidationOptions) { ); } +export namespace isEmpty { + export interface Options extends ValidationOptions {} +} + /** - * Checks if value is a not empty. Value should be string, array, set, map or object + * Checks if the value is not empty. Value should be string, array, set, map or object * @validator isNotEmpty */ -export function isNotEmpty(options?: ValidationOptions) { - return validator( +export function isNotEmpty(options?: isNotEmpty.Options) { + return validator( 'isNotEmpty', (input: unknown, context: Context, _this) => { if (input != null) { @@ -102,3 +104,7 @@ export function isNotEmpty(options?: ValidationOptions) { options, ); } + +export namespace isNotEmpty { + export interface Options extends ValidationOptions {} +} diff --git a/src/rules/logical-rules/is-equal.ts b/src/rules/logical-rules/is-equal.ts index 2429587..bc69abb 100644 --- a/src/rules/logical-rules/is-equal.ts +++ b/src/rules/logical-rules/is-equal.ts @@ -8,7 +8,7 @@ import { * * @validator isEqual */ -export function isEqual(compare: T, options?: ValidationOptions) { +export function isEqual(compare: T, options?: isEqual.Options) { return validator( 'isEqual', (input: unknown, context: Context, _this) => { @@ -21,11 +21,15 @@ export function isEqual(compare: T, options?: ValidationOptions) { ); } +export namespace isEqual { + export interface Options extends ValidationOptions {} +} + /** * * @validator isNotEqual */ -export function isNotEqual(compare: any, options?: ValidationOptions) { +export function isNotEqual(compare: any, options?: isNotEqual.Options) { return validator( 'isNotEqual', (input: unknown, context: Context, _this) => { @@ -38,3 +42,7 @@ export function isNotEqual(compare: any, options?: ValidationOptions) { options, ); } + +export namespace isNotEqual { + export interface Options extends ValidationOptions {} +} diff --git a/src/rules/logical-rules/is-gt.ts b/src/rules/logical-rules/is-gt.ts new file mode 100644 index 0000000..ccae890 --- /dev/null +++ b/src/rules/logical-rules/is-gt.ts @@ -0,0 +1,58 @@ +import { + type Context, + type Nullish, + type ValidationOptions, + type Validator, + validator, +} from '../../core/index.js'; +import type { range } from './range.js'; + +/** + * Checks if value is grater than "minValue" + * @validator iGt + */ +export function isGt( + minValue: T, + options?: isGt.Options, +): Validator { + return validator( + 'isGt', + (input: T, context: Context, _this): Nullish => { + if ( + (typeof minValue === 'number' || typeof minValue === 'bigint') && + (typeof input === 'number' || typeof input === 'bigint') && + input > minValue + ) { + return input; + } + if ( + minValue instanceof Date && + input instanceof Date && + input > minValue + ) { + return input; + } + if ( + typeof minValue === 'string' && + typeof input === 'string' && + (input > minValue || + (options?.caseInsensitive && + input.toLowerCase() > minValue.toLowerCase())) + ) { + return input; + } + context.fail( + _this, + `Value must be greater than ${typeof minValue === 'string' ? `"${minValue}"` : minValue}`, + input, + ); + }, + options, + ); +} + +export namespace isGt { + export interface Options extends ValidationOptions { + caseInsensitive?: boolean; + } +} diff --git a/src/rules/logical-rules/is-gte.ts b/src/rules/logical-rules/is-gte.ts new file mode 100644 index 0000000..9943c37 --- /dev/null +++ b/src/rules/logical-rules/is-gte.ts @@ -0,0 +1,58 @@ +import { + type Context, + type Nullish, + type ValidationOptions, + type Validator, + validator, +} from '../../core/index.js'; +import type { range } from './range.js'; + +/** + * Checks if value is grater than or equal to minValue + * @validator isGte + */ +export function isGte( + minValue: T, + options?: isGte.Options, +): Validator { + return validator( + 'isGte', + (input: T, context: Context, _this): Nullish => { + if ( + (typeof minValue === 'number' || typeof minValue === 'bigint') && + (typeof input === 'number' || typeof input === 'bigint') && + input >= minValue + ) { + return input; + } + if ( + minValue instanceof Date && + input instanceof Date && + input >= minValue + ) { + return input; + } + if ( + typeof minValue === 'string' && + typeof input === 'string' && + (input >= minValue || + (options?.caseInsensitive && + input.toLowerCase() >= minValue.toLowerCase())) + ) { + return input; + } + context.fail( + _this, + `Value must be greater than or equal to ${typeof minValue === 'string' ? `"${minValue}"` : minValue}`, + input, + ); + }, + options, + ); +} + +export namespace isGte { + export interface Options extends ValidationOptions { + caseInsensitive?: boolean; + } +} diff --git a/src/rules/logical-rules/is-lt.ts b/src/rules/logical-rules/is-lt.ts new file mode 100644 index 0000000..241305f --- /dev/null +++ b/src/rules/logical-rules/is-lt.ts @@ -0,0 +1,58 @@ +import { + type Context, + type Nullish, + type ValidationOptions, + type Validator, + validator, +} from '../../core/index.js'; +import type { range } from './range.js'; + +/** + * Checks if the value is lover than maxValue + * @validator isLt + */ +export function isLt( + maxValue: T, + options?: isLt.Options, +): Validator { + return validator( + 'isLt', + (input: T, context: Context, _this): Nullish => { + if ( + (typeof maxValue === 'number' || typeof maxValue === 'bigint') && + (typeof input === 'number' || typeof input === 'bigint') && + input < maxValue + ) { + return input; + } + if ( + maxValue instanceof Date && + input instanceof Date && + input < maxValue + ) { + return input; + } + if ( + typeof maxValue === 'string' && + typeof input === 'string' && + (input < maxValue || + (options?.caseInsensitive && + input.toLowerCase() < maxValue.toLowerCase())) + ) { + return input; + } + context.fail( + _this, + `Value must be lover than ${typeof maxValue === 'string' ? `"${maxValue}"` : maxValue}`, + input, + ); + }, + options, + ); +} + +export namespace isLt { + export interface Options extends ValidationOptions { + caseInsensitive?: boolean; + } +} diff --git a/src/rules/logical-rules/is-lte.ts b/src/rules/logical-rules/is-lte.ts new file mode 100644 index 0000000..bdd80c1 --- /dev/null +++ b/src/rules/logical-rules/is-lte.ts @@ -0,0 +1,58 @@ +import { + type Context, + type Nullish, + type ValidationOptions, + type Validator, + validator, +} from '../../core/index.js'; +import type { range } from './range.js'; + +/** + * Checks if the value is lover than or equal to maxValue + * @validator isLte + */ +export function isLte( + maxValue: T, + options?: isLte.Options, +): Validator { + return validator( + 'isLte', + (input: T, context: Context, _this): Nullish => { + if ( + (typeof maxValue === 'number' || typeof maxValue === 'bigint') && + (typeof input === 'number' || typeof input === 'bigint') && + input <= maxValue + ) { + return input; + } + if ( + maxValue instanceof Date && + input instanceof Date && + input <= maxValue + ) { + return input; + } + if ( + typeof maxValue === 'string' && + typeof input === 'string' && + (input <= maxValue || + (options?.caseInsensitive && + input.toLowerCase() <= maxValue.toLowerCase())) + ) { + return input; + } + context.fail( + _this, + `Value must be lover than or equal to ${typeof maxValue === 'string' ? `"${maxValue}"` : maxValue}`, + input, + ); + }, + options, + ); +} + +export namespace isLte { + export interface Options extends ValidationOptions { + caseInsensitive?: boolean; + } +} diff --git a/src/rules/logical-rules/length.ts b/src/rules/logical-rules/length.ts new file mode 100644 index 0000000..d6cabe2 --- /dev/null +++ b/src/rules/logical-rules/length.ts @@ -0,0 +1,33 @@ +import { allOf } from '../utility-rules/all-of.js'; +import { getLength } from '../utility-rules/get-length.js'; +import { pipe } from '../utility-rules/pipe.js'; +import { isGte } from './is-gte.js'; +import { isLte } from './is-lte.js'; + +/** + * Checks the length is at least "minValue" + * @validator lengthMin + */ +export const lengthMin = (minValue: number) => + allOf([ + pipe([ + getLength(), + isGte(minValue, { + onFail: () => `Value length must be at least ${minValue}`, + }), + ]), + ]); + +/** + * Checks if the length is at most "maxValue" + * @validator lengthMax + */ +export const lengthMax = (maxValue: number) => + allOf([ + pipe([ + getLength(), + isLte(maxValue, { + onFail: () => `The length of {{label}} must be at most ${maxValue}`, + }), + ]), + ]); diff --git a/src/rules/logical-rules/range.ts b/src/rules/logical-rules/range.ts index 63d6790..8eeaed1 100644 --- a/src/rules/logical-rules/range.ts +++ b/src/rules/logical-rules/range.ts @@ -2,27 +2,21 @@ import { type Context, type Nullish, type ValidationOptions, - type Validator, validator, } from '../../core/index.js'; -import { allOf } from '../utility-rules/all-of.js'; -import { getLength } from '../utility-rules/get-length.js'; -import { pipe } from '../utility-rules/pipe.js'; - -type RangeInput = number | bigint | Date | string; /** * Checks if value is between minValue and maxValue * @validator range */ -export function range( +export function range( minValue: T, maxValue: T, - options?: ValidationOptions, + options?: range.Options, ) { return validator( 'range', - (input: RangeInput, context: Context, _this): Nullish => { + (input: range.Input, context: Context, _this): Nullish => { if ( (typeof minValue === 'number' || typeof minValue === 'bigint') && (typeof maxValue === 'number' || typeof maxValue === 'bigint') && @@ -60,212 +54,7 @@ export function range( ); } -/** - * Checks if value is grater than "minValue" - * @validator iGt - */ -export function isGt( - minValue: T, - options?: ValidationOptions & { caseInsensitive?: boolean }, -): Validator { - return validator( - 'isGt', - (input: T, context: Context, _this): Nullish => { - if ( - (typeof minValue === 'number' || typeof minValue === 'bigint') && - (typeof input === 'number' || typeof input === 'bigint') && - input > minValue - ) { - return input; - } - if ( - minValue instanceof Date && - input instanceof Date && - input > minValue - ) { - return input; - } - if ( - typeof minValue === 'string' && - typeof input === 'string' && - (input > minValue || - (options?.caseInsensitive && - input.toLowerCase() > minValue.toLowerCase())) - ) { - return input; - } - context.fail( - _this, - `Value must be greater than ${typeof minValue === 'string' ? `"${minValue}"` : minValue}`, - input, - ); - }, - options, - ); -} - -// ************************************************************* - -/** - * Checks if value is grater than or equal to minValue - * @validator isGte - */ -export function isGte( - minValue: T, - options?: ValidationOptions & { caseInsensitive?: boolean }, -): Validator { - return validator( - 'isGte', - (input: T, context: Context, _this): Nullish => { - if ( - (typeof minValue === 'number' || typeof minValue === 'bigint') && - (typeof input === 'number' || typeof input === 'bigint') && - input >= minValue - ) { - return input; - } - if ( - minValue instanceof Date && - input instanceof Date && - input >= minValue - ) { - return input; - } - if ( - typeof minValue === 'string' && - typeof input === 'string' && - (input >= minValue || - (options?.caseInsensitive && - input.toLowerCase() >= minValue.toLowerCase())) - ) { - return input; - } - context.fail( - _this, - `Value must be greater than or equal to ${typeof minValue === 'string' ? `"${minValue}"` : minValue}`, - input, - ); - }, - options, - ); -} - -// ************************************************************* - -/** - * Checks if number value is lover than maxValue - * @validator isLt - */ -export function isLt( - maxValue: T, - options?: ValidationOptions & { caseInsensitive?: boolean }, -): Validator { - return validator( - 'isLt', - (input: T, context: Context, _this): Nullish => { - if ( - (typeof maxValue === 'number' || typeof maxValue === 'bigint') && - (typeof input === 'number' || typeof input === 'bigint') && - input < maxValue - ) { - return input; - } - if ( - maxValue instanceof Date && - input instanceof Date && - input < maxValue - ) { - return input; - } - if ( - typeof maxValue === 'string' && - typeof input === 'string' && - (input < maxValue || - (options?.caseInsensitive && - input.toLowerCase() < maxValue.toLowerCase())) - ) { - return input; - } - context.fail( - _this, - `Value must be lover than ${typeof maxValue === 'string' ? `"${maxValue}"` : maxValue}`, - input, - ); - }, - options, - ); -} - -// ************************************************************* - -/** - * Checks if value is lover than or equal to maxValue - * @validator isLte - */ -export function isLte( - maxValue: T, - options?: ValidationOptions & { caseInsensitive?: boolean }, -): Validator { - return validator( - 'isLte', - (input: T, context: Context, _this): Nullish => { - if ( - (typeof maxValue === 'number' || typeof maxValue === 'bigint') && - (typeof input === 'number' || typeof input === 'bigint') && - input <= maxValue - ) { - return input; - } - if ( - maxValue instanceof Date && - input instanceof Date && - input <= maxValue - ) { - return input; - } - if ( - typeof maxValue === 'string' && - typeof input === 'string' && - (input <= maxValue || - (options?.caseInsensitive && - input.toLowerCase() <= maxValue.toLowerCase())) - ) { - return input; - } - context.fail( - _this, - `Value must be lover than or equal to ${typeof maxValue === 'string' ? `"${maxValue}"` : maxValue}`, - input, - ); - }, - options, - ); +export namespace range { + export type Input = number | bigint | Date | string; + export type Options = ValidationOptions; } - -/** - * Checks the length is at least "minValue" - * @validator lengthMin - */ -export const lengthMin = (minValue: number) => - allOf([ - pipe([ - getLength(), - isGte(minValue, { - onFail: () => `Value length must be at least ${minValue}`, - }), - ]), - ]); - -/** - * Checks if the length is at most "maxValue" - * @validator lengthMax - */ -export const lengthMax = (maxValue: number) => - allOf([ - pipe([ - getLength(), - isLte(maxValue, { - onFail: () => `The length of {{label}} must be at most ${maxValue}`, - }), - ]), - ]); diff --git a/src/rules/type-rules/is-any.ts b/src/rules/type-rules/is-any.ts index 52f86af..0676f50 100644 --- a/src/rules/type-rules/is-any.ts +++ b/src/rules/type-rules/is-any.ts @@ -1,7 +1,7 @@ import { validator } from '../../core/index.js'; /** - * Does nothing, just returns original input value. + * Does nothing, just returns the original input value. * @validator isAny */ export const isAny = () => diff --git a/src/rules/type-rules/is-array.ts b/src/rules/type-rules/is-array.ts index 2de8715..062f5c2 100644 --- a/src/rules/type-rules/is-array.ts +++ b/src/rules/type-rules/is-array.ts @@ -8,13 +8,13 @@ import { } from '../../core/index.js'; /** - * Validates if value is "array" and applies validation for each item. - * Converts input value to array if coerce option is set to 'true'. + * Validates if the value is "array" and applies validation for each item. + * Converts input value to array if the coerce option is set to 'true'. * @validator isArray */ export function isArray( itemValidator?: Validator, - options?: ValidationOptions, + options?: isArray.Options, ) { return validator( 'isArray', @@ -57,3 +57,7 @@ export function isArray( options, ); } + +export namespace isArray { + export interface Options extends ValidationOptions {} +} diff --git a/src/rules/type-rules/is-bigint.ts b/src/rules/type-rules/is-bigint.ts index 14f154a..cfd2de5 100644 --- a/src/rules/type-rules/is-bigint.ts +++ b/src/rules/type-rules/is-bigint.ts @@ -7,10 +7,10 @@ import { /** * Validates if value is "BigInt". - * Converts input value to number if coerce option is set to 'true'. + * Converts input value to number if the coerce option is set to 'true'. * @validator isNumber */ -export function isBigint(options?: ValidationOptions) { +export function isBigint(options?: isBigint.Options) { return validator( 'isBigint', (input: unknown, context: Context, _this): Nullish => { @@ -27,3 +27,7 @@ export function isBigint(options?: ValidationOptions) { options, ); } + +export namespace isBigint { + export interface Options extends ValidationOptions {} +} diff --git a/src/rules/type-rules/is-boolean.ts b/src/rules/type-rules/is-boolean.ts index a2abbc9..3ca0bbf 100644 --- a/src/rules/type-rules/is-boolean.ts +++ b/src/rules/type-rules/is-boolean.ts @@ -10,10 +10,10 @@ const FALSE_PATTERN = /^false|f|0|no|n$/i; /** * Validates if value is "boolean". - * Converts input value to boolean if coerce option is set to 'true'. + * Converts input value to boolean if the coerce option is set to 'true'. * @validator isBoolean */ -export function isBoolean(options?: ValidationOptions) { +export function isBoolean(options?: isBoolean.Options) { return validator( 'isBoolean', (input: unknown, context: Context, _this): Nullish => { @@ -33,3 +33,7 @@ export function isBoolean(options?: ValidationOptions) { options, ); } + +export namespace isBoolean { + export interface Options extends ValidationOptions {} +} diff --git a/src/rules/type-rules/is-date.ts b/src/rules/type-rules/is-date.ts index 5cdbcb2..e95055e 100644 --- a/src/rules/type-rules/is-date.ts +++ b/src/rules/type-rules/is-date.ts @@ -6,33 +6,12 @@ import { validator, } from '../../core/index.js'; -export type DatePrecision = - | 'year' - | 'yr' - | 'month' - | 'mo' - | 'day' - | 'd' - | 'hours' - | 'hr' - | 'minutes' - | 'min' - | 'seconds' - | 'sec' - | 'milliseconds' - | 'ms' - | 'tz'; - -export interface IsDateOptions extends ValidationOptions { - trim?: DatePrecision; -} - /** * Validates if value is a "Date" instance or ISO 8601 formatted date string. * if a `coerce` option is `true`, converts input value to Date instance * @validator isDate */ -export function isDate(options?: IsDateOptions) { +export function isDate(options?: isDate.Options) { const trim = options?.trim; return validator( 'isDate', @@ -61,19 +40,35 @@ export function isDate(options?: IsDateOptions) { ); } -export interface IsDateStringOptions extends ValidationOptions { - precisionMin?: DatePrecision; - precisionMax?: DatePrecision; - trim?: boolean; - timeZone?: boolean | number; +export namespace isDate { + export type Precision = + | 'year' + | 'yr' + | 'month' + | 'mo' + | 'day' + | 'd' + | 'hours' + | 'hr' + | 'minutes' + | 'min' + | 'seconds' + | 'sec' + | 'milliseconds' + | 'ms' + | 'tz'; + + export interface Options extends ValidationOptions { + trim?: Precision; + } } /** - * Validates if value is DFS (date formatted string). + * Validates if value is DFS (date-formatted string). * Converts input value to DFS if the "coerce" option is set to 'true'. * @validator isDateString */ -export function isDateString(options?: IsDateStringOptions) { +export function isDateString(options?: isDateString.Options) { const trim = options?.trim; const precisionMax = options?.precisionMax || 'tz'; const precisionMaxIdx = PRECISION_INDEX[precisionMax] || 8; @@ -120,6 +115,16 @@ export function isDateString(options?: IsDateStringOptions) { ); } +export namespace isDateString { + export type Precision = isDate.Precision; + export interface Options extends ValidationOptions { + precisionMin?: Precision; + precisionMax?: Precision; + trim?: boolean; + timeZone?: boolean | number; + } +} + // noinspection RegExpUnnecessaryNonCapturingGroup const DATE_PATTERN = /^(\d{4})(?:-(0[0-9]|1[0-2]))?(?:-([0-2][0-9]|3[0-1]))?(?:[T ](?:([0-1][0-9]|2[0-4]):([0-5][0-9])(?::([0-5][0-9]))?(?:\.(\d{0,6}))?)?((?:[+-](0[0-9]|1[0-2])(?::(\d{2}))?)|Z)?)?$/; @@ -128,7 +133,7 @@ const DATE_PATTERN2 = function coerceDateString( input: any, - trimPrecision?: DatePrecision, + trimPrecision?: isDate.Precision, ): Nullish<{ value: string; precision: number; @@ -201,7 +206,7 @@ function coerceDateString( }; } -const PRECISION_INDEX: Record = { +const PRECISION_INDEX: Record = { year: 1, yr: 1, month: 2, diff --git a/src/rules/type-rules/is-enum.ts b/src/rules/type-rules/is-enum.ts index f512ca9..f52ebe0 100644 --- a/src/rules/type-rules/is-enum.ts +++ b/src/rules/type-rules/is-enum.ts @@ -6,15 +6,12 @@ import { } from '../../core/index.js'; /** - * Validates if given value is one of enum values. + * Validates if the given value is one of enum values. * @validator isEnum */ export function isEnum( values: any, - options?: ValidationOptions & { - caseInSensitive?: boolean; - enumName?: string; - }, + options?: isEnum.Options, ): Validator { const caseInSensitive = !!options?.caseInSensitive; const enumName = options?.enumName; @@ -60,3 +57,10 @@ export function isEnum( options, ); } + +export namespace isEnum { + export interface Options extends ValidationOptions { + caseInSensitive?: boolean; + enumName?: string; + } +} diff --git a/src/rules/type-rules/is-integer.ts b/src/rules/type-rules/is-integer.ts index e0da702..fc29ad6 100644 --- a/src/rules/type-rules/is-integer.ts +++ b/src/rules/type-rules/is-integer.ts @@ -6,11 +6,11 @@ import { } from '../../core/index.js'; /** - * Validates if value is "integer". - * Converts input value to integer number if coerce option is set to 'true'. + * Validates if the value is "integer". + * Converts the input value to an integer number if the coerce option is set to 'true'. * @validator isInteger */ -export function isInteger(options?: ValidationOptions) { +export function isInteger(options?: isInteger.Options) { return validator( 'isInteger', (input: unknown, context: Context, _this): Nullish => { @@ -35,3 +35,7 @@ export function isInteger(options?: ValidationOptions) { options, ); } + +export namespace isInteger { + export interface Options extends ValidationOptions {} +} diff --git a/src/rules/type-rules/is-null.ts b/src/rules/type-rules/is-null.ts index a1efe0f..8cc5bf3 100644 --- a/src/rules/type-rules/is-null.ts +++ b/src/rules/type-rules/is-null.ts @@ -5,10 +5,10 @@ import { } from '../../core/index.js'; /** - * Validates if value is "null". + * Validates if the value is "null". * @validator isNull */ -export function isNull(options?: ValidationOptions) { +export function isNull(options?: isNull.Options) { return validator( 'isNull', (input: unknown, context: Context, _this) => { @@ -19,11 +19,15 @@ export function isNull(options?: ValidationOptions) { ); } +export namespace isNull { + export interface Options extends ValidationOptions {} +} + /** - * Validates if value is not "null". + * Validates if the value is not "null". * @validator isNotNull */ -export function isNotNull(options?: ValidationOptions) { +export function isNotNull(options?: isNotNull.Options) { return validator( 'isNotNull', (input: unknown, context: Context, _this) => { @@ -34,11 +38,15 @@ export function isNotNull(options?: ValidationOptions) { ); } +export namespace isNotNull { + export interface Options extends ValidationOptions {} +} + /** - * Validates if value is "null" or "undefined". + * Validates if the value is "null" or "undefined". * @validator isNullish */ -export function isNullish(options?: ValidationOptions) { +export function isNullish(options?: isNullish.Options) { return validator( 'isNullish', (input: unknown, context: Context, _this) => { @@ -49,11 +57,15 @@ export function isNullish(options?: ValidationOptions) { ); } +export namespace isNullish { + export interface Options extends ValidationOptions {} +} + /** - * Validates if value is not "null" nor "undefined". + * Validates if the value is not "null" nor "undefined". * @validator isNotNullish */ -export function isNotNullish(options?: ValidationOptions) { +export function isNotNullish(options?: isNotNullish.Options) { return validator( 'isNotNullish', (input: unknown, context: Context, _this) => { @@ -64,3 +76,7 @@ export function isNotNullish(options?: ValidationOptions) { options, ); } + +export namespace isNotNullish { + export interface Options extends ValidationOptions {} +} diff --git a/src/rules/type-rules/is-number.ts b/src/rules/type-rules/is-number.ts index e935926..da75627 100644 --- a/src/rules/type-rules/is-number.ts +++ b/src/rules/type-rules/is-number.ts @@ -6,11 +6,11 @@ import { } from '../../core/index.js'; /** - * Validates if value is "number". - * Converts input value to number if coerce option is set to 'true'. + * Validates if the value is a number. + * Converts input value to number if the coerce option is set to 'true'. * @validator isNumber */ -export function isNumber(options?: ValidationOptions) { +export function isNumber(options?: isNumber.Options) { return validator( 'isNumber', (input: unknown, context: Context, _this): Nullish => { @@ -30,3 +30,7 @@ export function isNumber(options?: ValidationOptions) { options, ); } + +export namespace isNumber { + export interface Options extends ValidationOptions {} +} diff --git a/src/rules/type-rules/is-object.ts b/src/rules/type-rules/is-object.ts index a718789..db6be7a 100644 --- a/src/rules/type-rules/is-object.ts +++ b/src/rules/type-rules/is-object.ts @@ -10,38 +10,15 @@ import { validator, } from '../../core/index.js'; -export namespace IsObject { - export interface Validator< - T extends object = object, - I = object, - > extends Validator_ { - schema: Schema; - } - - export type PropertyOptions = { label?: string; as?: string }; - export type Schema = Record< - string | number, - Validator_ | [Validator_, PropertyOptions] - >; - - export interface Options extends ValidationOptions { - name?: string; - ctor?: Type; - additionalFields?: boolean | Validator_ | 'error'; - caseInSensitive?: boolean; - detectCircular?: boolean; - } -} - /** - * Validates object according to schema - * Converts properties according to schema rules if coerce option is set to 'true'. + * Validates the object according to schema + * Converts properties according to schema rules if the coerce option is set to 'true'. * @validator isObject */ export function isObject( - schema?: IsObject.Schema, - options?: IsObject.Options, -): IsObject.Validator { + schema?: isObject.Schema, + options?: isObject.Options, +): isObject.Validator { const ctor = options?.ctor; const ctorName = options?.name || ctor?.name; const additionalFields = options?.additionalFields ?? !schema; @@ -50,7 +27,7 @@ export function isObject( const propertyRules: Record = {}; const propertyOptions: Record< any, - RequiredSome + RequiredSome > = {}; schema = schema || {}; const schemaKeys = Object.keys(schema); @@ -158,8 +135,31 @@ export function isObject( return out; }, options, - ) as unknown as IsObject.Validator; + ) as unknown as isObject.Validator; _rule.schema = schema; return _rule; } + +export namespace isObject { + export interface Validator< + T extends object = object, + I = object, + > extends Validator_ { + schema: Schema; + } + + export type PropertyOptions = { label?: string; as?: string }; + export type Schema = Record< + string | number, + Validator_ | [Validator_, PropertyOptions] + >; + + export interface Options extends ValidationOptions { + name?: string; + ctor?: Type; + additionalFields?: boolean | Validator_ | 'error'; + caseInSensitive?: boolean; + detectCircular?: boolean; + } +} diff --git a/src/rules/type-rules/is-record.ts b/src/rules/type-rules/is-record.ts index f847576..d0bb72c 100644 --- a/src/rules/type-rules/is-record.ts +++ b/src/rules/type-rules/is-record.ts @@ -8,14 +8,14 @@ import { } from '../../core/index.js'; /** - * Validates record object according to given "key" and "value" rules - * Converts properties according to rules if coerce option is set to 'true'. + * Validates the record object according to given "key" and "value" rules + * Converts properties according to rules if the coerce option is set to 'true'. * @validator isRecord */ export function isRecord( keyRule: Validator, valueRule: Validator, - options?: ValidationOptions, + options?: isRecord.Options, ) { return validator>( 'isRecord', @@ -61,3 +61,7 @@ export function isRecord( options, ); } + +export namespace isRecord { + export interface Options extends ValidationOptions {} +} diff --git a/src/rules/type-rules/is-string.ts b/src/rules/type-rules/is-string.ts index 279c6ae..89ac4b8 100644 --- a/src/rules/type-rules/is-string.ts +++ b/src/rules/type-rules/is-string.ts @@ -6,11 +6,11 @@ import { } from '../../core/index.js'; /** - * Validates if value is "string". - * Converts input value to string if coerce option is set to 'true'. + * Validates if the value is a string. + * Converts input value to string if the oerce option is set to 'true'. * @validator isString */ -export function isString(options?: ValidationOptions) { +export function isString(options?: isString.Options) { return validator( 'isString', (input: any, context: Context, _this): Nullish => { @@ -29,98 +29,6 @@ export function isString(options?: ValidationOptions) { ); } -/** - * Process "String.replace" method - * @validator stringReplace - */ -export function stringReplace( - searchValue: string | RegExp, - replaceValue: string, -); -export function stringReplace( - searchValue: string | RegExp, - replacer: (subsring: string, ...args: any[]) => string, -); -export function stringReplace( - searchValue: { - [Symbol.replace](string: string, replaceValue: string): string; - }, - replaceValue: string, -); -export function stringReplace( - searchValue: { - [Symbol.replace]( - string: string, - replacer: (substring: string, ...args: any[]) => string, - ): string; - }, - replacer: (substring: string, ...args: any[]) => string, -); -export function stringReplace(searchValue: any, replacer: any) { - return validator( - 'stringReplace', - (input: unknown): Nullish => { - if (input == null) return input; - return String(input).replace(searchValue, replacer); - }, - ); -} - -/** - * Process "String.split" method - * @validator split - */ -export function stringSplit(separator: string | RegExp, limit?: number); -export function stringSplit( - splitter: { [Symbol.split](string: string, limit?: number): string[] }, - limit?: number, -); -export function stringSplit(splitter: any, limit: any) { - return validator('stringSplit', (input: unknown) => { - if (input == null) return input; - return String(input).split(splitter, limit); - }); -} - -/** - * Removes whitespace from both ends of a string - * @validator trim - */ -export function trim() { - return validator('trim', (input: unknown) => { - if (input == null) return input; - return String(input).trim(); - }); +export namespace isString { + export interface Options extends ValidationOptions {} } - -// ************************************************************* - -/** - * Removes whitespace from the end of a string - * @validator trimEnd - */ -export const trimEnd = () => trimEndRule; - -const trimEndRule = validator( - 'trimEnd', - (input: unknown): Nullish => { - if (input == null) return input; - return String(input).trimEnd(); - }, -); - -// ************************************************************* - -/** - * Removes whitespace from the beginning of a string - * @validator trimStart - */ -export const trimStart = () => trimStartRule; - -const trimStartRule = validator( - 'trimStart', - (input: unknown): Nullish => { - if (input == null) return input; - return String(input).trimStart(); - }, -); diff --git a/src/rules/type-rules/is-tuple.ts b/src/rules/type-rules/is-tuple.ts index 0a5055e..309d65d 100644 --- a/src/rules/type-rules/is-tuple.ts +++ b/src/rules/type-rules/is-tuple.ts @@ -6,21 +6,21 @@ import { } from '../../core/index.js'; /** - * Validates if value is "tuple" and applies validation for each item. - * Converts input value to tuple if coerce option is set to 'true'. + * Validates if the value is "tuple" and applies validation for each item. + * Converts input value to tuple if the coerce option is set to 'true'. * @validator isTuple */ export function isTuple( items: [Validator], - options?: ValidationOptions, + options?: isTuple.Options, ): Validator<[T1], [I1]>; export function isTuple( items: [Validator, Validator], - options?: ValidationOptions, + options?: isTuple.Options, ): Validator<[T1, T2], [I1, I2]>; export function isTuple( items: [Validator, Validator, Validator], - options?: ValidationOptions, + options?: isTuple.Options, ): Validator<[T1, T2, T3], [I1, I2, I3]>; export function isTuple( items: [ @@ -29,7 +29,7 @@ export function isTuple( Validator, Validator, ], - options?: ValidationOptions, + options?: isTuple.Options, ): Validator<[T1, T2, T3, T4], [I1, I2, I3, I4]>; export function isTuple( items: [ @@ -38,7 +38,7 @@ export function isTuple( Validator, Validator, ], - options?: ValidationOptions, + options?: isTuple.Options, ): Validator<[T1, T2, T3, T4], [I1, I2, I3, I4]>; export function isTuple( items: [ @@ -48,7 +48,7 @@ export function isTuple( Validator, Validator, ], - options?: ValidationOptions, + options?: isTuple.Options, ): Validator<[T1, T2, T3, T4, T5], [I1, I2, I3, I4, I5]>; export function isTuple( items: [ @@ -60,7 +60,7 @@ export function isTuple( Validator, ...Validator[], ], - options?: ValidationOptions, + options?: isTuple.Options, ): Validator< [T1, T2, T3, T4, T5, T6, ...any[]], [I1, I2, I3, I4, I5, I5, ...any[]] @@ -102,3 +102,7 @@ export function isTuple(items: Validator[], options?: ValidationOptions) { options, ); } + +export namespace isTuple { + export interface Options extends ValidationOptions {} +} diff --git a/src/rules/type-rules/is-undefined.ts b/src/rules/type-rules/is-undefined.ts index 3938318..2403308 100644 --- a/src/rules/type-rules/is-undefined.ts +++ b/src/rules/type-rules/is-undefined.ts @@ -6,10 +6,10 @@ import { } from '../../core/index.js'; /** - * Validates if value is "undefined" + * Validates if the value is undefined * @validator isUndefined */ -export function isUndefined(options?: ValidationOptions) { +export function isUndefined(options?: isUndefined.Options) { return validator( 'isUndefined', (input: unknown, context: Context, _this): Nullish => { @@ -20,3 +20,7 @@ export function isUndefined(options?: ValidationOptions) { options, ); } + +export namespace isUndefined { + export interface Options extends ValidationOptions {} +} diff --git a/src/rules/utility-rules/all-of.ts b/src/rules/utility-rules/all-of.ts index 020c6da..9951301 100644 --- a/src/rules/utility-rules/all-of.ts +++ b/src/rules/utility-rules/all-of.ts @@ -1,18 +1,30 @@ -import { type Context, type Validator, validator } from '../../core/index.js'; +import { + type Context, + ValidationOptions, + type Validator, + validator, +} from '../../core/index.js'; /** * Test given value against to all codecs and returns original input * @validator allOf */ -export function allOf(rules: Validator[]): Validator { +export function allOf( + rules: Validator[], + options?: allOf.Options, +): Validator { return validator('allOf', (input: any, context: Context): any => { let i: number; let c: Validator; const l = rules.length; for (i = 0; i < l; i++) { c = rules[i]; - c(input, context); + c(input, context || options); } return input; }); } + +export namespace allOf { + export interface Options extends ValidationOptions {} +} diff --git a/src/rules/utility-rules/exists.ts b/src/rules/utility-rules/exists.ts index 9afd7f6..3753eb8 100644 --- a/src/rules/utility-rules/exists.ts +++ b/src/rules/utility-rules/exists.ts @@ -8,7 +8,7 @@ import { * Validates if property exists * @validator exists */ -export function exists(options?: ValidationOptions) { +export function exists(options?: exists.Options) { return validator( 'exists', (input: unknown, context: Context, _this) => { @@ -24,3 +24,7 @@ export function exists(options?: ValidationOptions) { options, ); } + +export namespace exists { + export interface Options extends ValidationOptions {} +} diff --git a/src/rules/utility-rules/one-of.ts b/src/rules/utility-rules/one-of.ts index 14fb09d..5bdcf63 100644 --- a/src/rules/utility-rules/one-of.ts +++ b/src/rules/utility-rules/one-of.ts @@ -1,11 +1,19 @@ -import { type Context, type Validator, validator } from '../../core/index.js'; +import { + type Context, + ValidationOptions, + type Validator, + validator, +} from '../../core/index.js'; type DiscriminatorRecord = Record; /** * */ -export function oneOf(rules: (Validator | [Validator, DiscriminatorRecord])[]) { +export function oneOf( + rules: (Validator | [Validator, DiscriminatorRecord])[], + options?: oneOf.Options, +) { const l = rules.length; return validator('union', (input: any, context: Context, _this): any => { let i: number; @@ -31,7 +39,7 @@ export function oneOf(rules: (Validator | [Validator, DiscriminatorRecord])[]) { } try { for (const k of Object.keys(discriminator)) { - discriminator[k](input[k], context); + discriminator[k](input[k], context || options); if (!passed) break; } if (!passed) continue; @@ -41,7 +49,7 @@ export function oneOf(rules: (Validator | [Validator, DiscriminatorRecord])[]) { } else c = rules[i] as Validator; if (passed) try { - v = c(input, context); + v = c(input, context || options); if (passed) break; } catch { // @@ -53,3 +61,7 @@ export function oneOf(rules: (Validator | [Validator, DiscriminatorRecord])[]) { context.fail(_this, `Value didn't match one of required rules`, input); }); } + +export namespace oneOf { + export interface Options extends ValidationOptions {} +} diff --git a/src/rules/utility-rules/optional.ts b/src/rules/utility-rules/optional.ts index 8fb184e..6a27cab 100644 --- a/src/rules/utility-rules/optional.ts +++ b/src/rules/utility-rules/optional.ts @@ -7,12 +7,12 @@ import { } from '../../core/index.js'; /** - * Makes sub-rule optional + * Makes the sub-rule optional * @validator optional */ export function optional( nested: Validator, - options?: ValidationOptions, + options?: optional.Options, ) { return validator, Nullish>( 'optional', @@ -23,3 +23,7 @@ export function optional( options, ); } + +export namespace optional { + export interface Options extends ValidationOptions {} +} diff --git a/src/rules/utility-rules/pipe.ts b/src/rules/utility-rules/pipe.ts index 45fb07d..656158a 100644 --- a/src/rules/utility-rules/pipe.ts +++ b/src/rules/utility-rules/pipe.ts @@ -6,17 +6,13 @@ import { validator, } from '../../core/index.js'; -export interface PipeOptions extends ValidationOptions { - returnIndex?: number; -} - /** * * @validator pipe */ export function pipe( rules: Validator[], - options?: PipeOptions, + options?: pipe.Options, ): Validator { const l = rules.length; const returnIndex = options?.returnIndex; @@ -39,3 +35,9 @@ export function pipe( options, ); } + +export namespace pipe { + export interface Options extends ValidationOptions { + returnIndex?: number; + } +} diff --git a/src/rules/utility-rules/string-utils.ts b/src/rules/utility-rules/string-utils.ts new file mode 100644 index 0000000..87d37d7 --- /dev/null +++ b/src/rules/utility-rules/string-utils.ts @@ -0,0 +1,97 @@ +import { type Nullish, validator } from '../../core/index.js'; + +/** + * Applies "String.replace" method + * @validator stringReplace + */ +export function stringReplace( + searchValue: string | RegExp, + replaceValue: string, +); +export function stringReplace( + searchValue: string | RegExp, + replacer: (subsring: string, ...args: any[]) => string, +); +export function stringReplace( + searchValue: { + [Symbol.replace](string: string, replaceValue: string): string; + }, + replaceValue: string, +); +export function stringReplace( + searchValue: { + [Symbol.replace]( + string: string, + replacer: (substring: string, ...args: any[]) => string, + ): string; + }, + replacer: (substring: string, ...args: any[]) => string, +); +export function stringReplace(searchValue: any, replacer: any) { + return validator( + 'stringReplace', + (input: unknown): Nullish => { + if (input == null) return input; + return String(input).replace(searchValue, replacer); + }, + ); +} + +/** + * Applies "String.split" method + * @validator split + */ +export function stringSplit(separator: string | RegExp, limit?: number); +export function stringSplit( + splitter: { [Symbol.split](string: string, limit?: number): string[] }, + limit?: number, +); +export function stringSplit(splitter: any, limit: any) { + return validator('stringSplit', (input: unknown) => { + if (input == null) return input; + return String(input).split(splitter, limit); + }); +} + +/** + * Removes whitespace from both ends of a string + * @validator trim + */ +export function trim() { + return validator('trim', (input: unknown) => { + if (input == null) return input; + return String(input).trim(); + }); +} + +// ************************************************************* + +/** + * Removes whitespace from the end of a string + * @validator trimEnd + */ +export const trimEnd = () => trimEndRule; + +const trimEndRule = validator( + 'trimEnd', + (input: unknown): Nullish => { + if (input == null) return input; + return String(input).trimEnd(); + }, +); + +// ************************************************************* + +/** + * Removes whitespace from the beginning of a string + * @validator trimStart + */ +export const trimStart = () => trimStartRule; + +const trimStartRule = validator( + 'trimStart', + (input: unknown): Nullish => { + if (input == null) return input; + return String(input).trimStart(); + }, +);