Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.ko.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ app.listen(3000)
| `@IsLowercase(message?: string)` | 필드에 소문자만 포함되어야 합니다. | `@IsLowercase() username!: string` |
| `@IsJwt(message?: string)` | 필드가 유효한 JSON Web Token(JWT)인지 검증합니다. Base64URL 문자로 이루어진 `header.payload.signature` 형식이어야 합니다. | `@IsJwt() accessToken!: string` |
| `@IsUrl(options?: IsUrlOptions, message?: string)` | 필드가 유효한 URL인지 검증합니다. 기본적으로 `http`, `https`, `ftp` 프로토콜을 허용하며, `options.protocols`로 변경할 수 있습니다. | `@IsUrl() website!: string` |
| `@IsPhoneNumber(region?: CountryCode, message?: string)` | 필드가 유효한 전화번호인지 검증합니다. libphonenumber-js를 사용해 전 세계 모든 국가를 지원합니다. 지역 코드 지정 시 로컬 형식도 허용됩니다. | `@IsPhoneNumber('KR') phone!: string` |
| `@IsTimeZone(message?: string)` | 필드가 유효한 IANA 타임존 식별자인지 검증합니다(예: `Asia/Seoul`, `UTC`). 내장 `Intl` API를 사용합니다. | `@IsTimeZone() tz!: string` |
| `@IsHexColor(message?: string)` | 필드가 유효한 16진수 색상 코드인지 검증합니다. `#RGB`, `#RGBA`, `#RRGGBB`, `#RRGGBBAA` 형식을 지원합니다(대소문자 구분 없음). | `@IsHexColor() color!: string` |
| `@IsHexadecimal(message?: string)` | 필드가 16진수 숫자인지 검증합니다. `0-9` 및 `a-f` 문자(대소문자 구분 없음)만 허용되며, `0x` 접두사도 허용됩니다. | `@IsHexadecimal() color!: string` |
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ Full guide and API reference:
| `@IsLowercase(message?: string)` | Validates that the field contains only lowercase characters. | `@IsLowercase() username!: string` |
| `@IsJwt(message?: string)` | Validates that the field is a valid JSON Web Token (JWT) in `header.payload.signature` format using Base64URL characters. | `@IsJwt() accessToken!: string` |
| `@IsUrl(options?: IsUrlOptions, message?: string)` | Validates that the field is a valid URL. `http`, `https`, and `ftp` protocols are allowed by default. Use `options.protocols` to customize allowed protocols. | `@IsUrl() website!: string` |
| `@IsPhoneNumber(region?: CountryCode, message?: string)` | Validates that the field is a valid phone number using libphonenumber-js. Supports all countries. With a region, local formats are accepted. | `@IsPhoneNumber('KR') phone!: string` |
| `@IsTimeZone(message?: string)` | Validates that the field is a valid IANA timezone identifier (e.g., `Asia/Seoul`, `UTC`). Uses the built-in `Intl` API. | `@IsTimeZone() tz!: string` |
| `@IsHexColor(message?: string)` | Validates that the field is a valid hex color code. Supports `#RGB`, `#RGBA`, `#RRGGBB`, and `#RRGGBBAA` formats (case-insensitive). The `#` prefix is required. | `@IsHexColor() color!: string` |
| `@IsHexadecimal(message?: string)` | Validates that the field is a hexadecimal number (characters `0-9` and `a-f`, case-insensitive). The `0x` prefix is also allowed. | `@IsHexadecimal() color!: string` |
Expand Down
9 changes: 9 additions & 0 deletions apps/docs/docs/decorators/validators.md
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,15 @@ Validates that the decorated field is a valid URL. By default, `http`, `https`,
- **`protocols`**: An array of allowed protocols. Defaults to `['http', 'https', 'ftp']`.
- **`message`** (optional): The error message to display when validation fails. If omitted, a default message will be used.

### `@IsPhoneNumber(region?: CountryCode, message?: string)`

Validates that the decorated field is a valid phone number. Uses [libphonenumber-js](https://github.com/catamphetamine/libphonenumber-js) for accurate validation across all countries.

When a region is provided, local formats (without country code) are accepted. When no region is provided, the number must include a country code (e.g., `+82`). If the number includes a `+` country code prefix, the region parameter is ignored and the number is validated against its own country code.

- **`region`** (optional): ISO 3166-1 alpha-2 region code (e.g., `'KR'`, `'US'`). If omitted, the number must be in international format.
- **`message`** (optional): The error message to display when validation fails. If omitted, a default message will be used.

### `@IsTimeZone(message?: string)`

Validates that the decorated field is a valid IANA timezone identifier (e.g., `Asia/Seoul`, `America/New_York`, `UTC`). Uses the built-in `Intl` API for validation — no external dependencies required.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,15 @@ Validiert, dass das dekorierte Feld eine gültige URL ist. Standardmäßig sind
- **`protocols`**: Ein Array erlaubter Protokolle. Standard: `['http', 'https', 'ftp']`.
- **`message`** (optional): Die Fehlermeldung, die angezeigt wird, wenn die Validierung fehlschlägt. Wenn weggelassen, wird eine Standardmeldung verwendet.

### `@IsPhoneNumber(region?: CountryCode, message?: string)`

Validiert, dass das dekorierte Feld eine gültige Telefonnummer ist. Verwendet [libphonenumber-js](https://github.com/catamphetamine/libphonenumber-js) für eine genaue Validierung aller Länder.

Bei Angabe einer Region werden auch lokale Formate ohne Ländervorwahl akzeptiert. Ohne Region muss die Nummer eine Ländervorwahl enthalten (z. B. `+82`). Nummern mit `+`-Präfix werden anhand des eigenen Ländercodes validiert — der region-Parameter wird ignoriert.

- **`region`** (optional): ISO 3166-1 Alpha-2-Regionscode (z. B. `'KR'`, `'US'`). Wenn weggelassen, wird das internationale Format erwartet.
- **`message`** (optional): Die Fehlermeldung, die angezeigt wird, wenn die Validierung fehlschlägt. Wenn weggelassen, wird eine Standardmeldung verwendet.

### `@IsTimeZone(message?: string)`

Validiert, dass das dekorierte Feld ein gültiger IANA-Zeitzonenbezeichner ist (z. B. `Asia/Seoul`, `America/New_York`, `UTC`). Verwendet die eingebaute `Intl`-API zur Validierung — keine externen Abhängigkeiten erforderlich.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,15 @@ Valide que le champ décoré est une URL valide. Par défaut, les protocoles `ht
- **`protocols`** : Un tableau des protocoles autorisés. Par défaut : `['http', 'https', 'ftp']`.
- **`message`** (optionnel) : Le message d'erreur à afficher lorsque la validation échoue. S'il est omis, un message par défaut sera utilisé.

### `@IsPhoneNumber(region?: CountryCode, message?: string)`

Valide que le champ décoré est un numéro de téléphone valide. Utilise [libphonenumber-js](https://github.com/catamphetamine/libphonenumber-js) pour une validation précise dans tous les pays.

Avec un code région, les formats locaux sans indicatif pays sont acceptés. Sans région, le numéro doit inclure un indicatif pays (ex. : `+82`). Les numéros commençant par `+` sont validés selon leur propre indicatif pays, indépendamment du paramètre region.

- **`region`** (optionnel) : Code région ISO 3166-1 alpha-2 (ex. : `'KR'`, `'US'`). S'il est omis, le format international est requis.
- **`message`** (optionnel) : Le message d'erreur à afficher lorsque la validation échoue. S'il est omis, un message par défaut sera utilisé.

### `@IsTimeZone(message?: string)`

Valide que le champ décoré est un identifiant de fuseau horaire IANA valide (ex. : `Asia/Seoul`, `America/New_York`, `UTC`). Utilise l'API `Intl` intégrée — aucune dépendance externe requise.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,15 @@ Express-Cargo は、クラスにバインドされた受信リクエストデー
- **`protocols`**: 許可されるプロトコルの配列。デフォルトは `['http', 'https', 'ftp']` です。
- **`message`**(オプション): バリデーション失敗時に表示するエラーメッセージ。省略すると、デフォルトメッセージが使用されます。

### `@IsPhoneNumber(region?: CountryCode, message?: string)`

デコレートされたフィールドが有効な電話番号であることを検証します。[libphonenumber-js](https://github.com/catamphetamine/libphonenumber-js) を使用して、全世界の国の番号を正確に検証します。

地域コードを指定すると、国番号なしのローカル形式も許可されます。地域コードを省略すると、国番号を含む国際形式(例: `+82`)が必要です。`+` で始まる番号は region パラメータに関係なく、番号自体の国コードで検証されます。

- **`region`**(オプション): ISO 3166-1 alpha-2 地域コード(例: `'KR'`、`'US'`)。省略すると国際形式が要求されます。
- **`message`**(オプション): バリデーション失敗時に表示するエラーメッセージ。省略すると、デフォルトメッセージが使用されます。

### `@IsTimeZone(message?: string)`

デコレートされたフィールドが有効な IANA タイムゾーン識別子であることを検証します(例: `Asia/Seoul`、`America/New_York`、`UTC`)。Node.js 組み込みの `Intl` API を使用して検証するため、外部依存関係は不要です。
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,15 @@ title: 유효성 검사 데코레이터
- **`protocols`**: 허용할 프로토콜 배열. 기본값은 `['http', 'https', 'ftp']`.
- **`message`** (선택 사항): 검증 실패 시 표시할 메시지. 생략하면 기본 메시지가 사용됩니다.

### `@IsPhoneNumber(region?: CountryCode, message?: string)`

데코레이터가 적용된 필드가 유효한 전화번호인지 검증합니다. [libphonenumber-js](https://github.com/catamphetamine/libphonenumber-js)를 사용해 전 세계 모든 국가의 번호를 정확하게 검증합니다.

지역 코드를 지정하면 국가 코드 없는 로컬 형식도 허용됩니다. 지역 코드를 생략하면 국가 코드 포함(예: `+82`) 국제 형식이어야 합니다. `+`로 시작하는 번호는 region 파라미터와 무관하게 번호 자체의 국가 코드로 검증됩니다.

- **`region`** (선택 사항): ISO 3166-1 alpha-2 지역 코드(예: `'KR'`, `'US'`). 생략하면 국제 형식이 요구됩니다.
- **`message`** (선택 사항): 검증 실패 시 표시할 메시지. 생략하면 기본 메시지가 사용됩니다.

### `@IsTimeZone(message?: string)`

데코레이터가 적용된 필드가 유효한 IANA 타임존 식별자인지 검증합니다(예: `Asia/Seoul`, `America/New_York`, `UTC`). 내장 `Intl` API를 사용하여 검증하므로 외부 의존성이 필요 없습니다.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,15 @@ Express-Cargo использует декораторы для валидаци

- **`message`** (необязательно): Сообщение об ошибке, которое будет отображаться при сбое валидации. Если опущено, будет использоваться сообщение по умолчанию.

### `@IsPhoneNumber(region?: CountryCode, message?: string)`

Проверяет, что декорированное поле является допустимым номером телефона. Использует [libphonenumber-js](https://github.com/catamphetamine/libphonenumber-js) для точной валидации номеров всех стран.

При указании региона допускаются локальные форматы без кода страны. Без региона номер должен содержать код страны (например, `+82`). Номера с префиксом `+` валидируются по собственному коду страны, параметр region игнорируется.

- **`region`** (необязательно): Код региона по ISO 3166-1 alpha-2 (например, `'KR'`, `'US'`). Если опущено, требуется международный формат.
- **`message`** (необязательно): Сообщение об ошибке, которое будет отображаться при сбое валидации. Если опущено, будет использоваться сообщение по умолчанию.

### `@IsHexColor(message?: string)`

Проверяет, что декорированное поле является допустимым шестнадцатеричным кодом цвета. Поддерживаются форматы `#RGB`, `#RGBA`, `#RRGGBB` и `#RRGGBBAA` (без учёта регистра). Префикс `#` обязателен.
Expand Down
12 changes: 12 additions & 0 deletions apps/example/src/routers/validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
IsLowercase,
IsJwt,
IsUrl,
IsPhoneNumber,
IsHexColor,
IsTimeZone,
IsHexadecimal,
Expand Down Expand Up @@ -338,6 +339,17 @@ router.post('/is-url', bindingCargo(IsUrlExample), (req, res) => {
res.json(cargo)
})

class IsPhoneNumberExample {
@Body()
@IsPhoneNumber('KR')
phone!: string
}

router.post('/is-phone-number', bindingCargo(IsPhoneNumberExample), (req, res) => {
const cargo = getCargo<IsPhoneNumberExample>(req)
res.json(cargo)
})

class IsTimeZoneExample {
@Body()
@IsTimeZone()
Expand Down
3 changes: 3 additions & 0 deletions packages/express-cargo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,8 @@
"@types/express": ">=4.17.0 <6",
"express": ">=4.17.0 <6",
"reflect-metadata": "^0.2.2"
},
"dependencies": {
"libphonenumber-js": "^1.12.41"
}
}
22 changes: 22 additions & 0 deletions packages/express-cargo/src/validator.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ArrayComparator, cargoErrorMessage, EachValidatorRule, HashAlgorithm, IsUrlOptions, TypedPropertyDecorator, UuidVersion, ValidatorRule } from './types'
import { CargoClassMetadata } from './metadata'
import { isDeepEqual } from './utils'
import { isValidPhoneNumber, CountryCode } from 'libphonenumber-js'

function addValidator(target: any, propertyKey: string | symbol, rule: ValidatorRule) {
const classMeta = new CargoClassMetadata(target)
Expand Down Expand Up @@ -459,6 +460,27 @@ export function IsJwt(message?: cargoErrorMessage): TypedPropertyDecorator<strin
}
}

/**
* Checks if the string is a valid phone number for the given region.
* If no region is provided, validates as an international number (must include country code).
* @param region - Optional ISO 3166-1 alpha-2 region code (e.g., 'KR', 'US').
* @param message - Optional custom error message.
*/
export function IsPhoneNumber(region?: CountryCode, message?: cargoErrorMessage): TypedPropertyDecorator<string> {
return (target, propertyKey): void => {
addValidator(
target,
propertyKey,
new ValidatorRule(
propertyKey,
'isPhoneNumber',
(value: unknown) => typeof value === 'string' && isValidPhoneNumber(value, region),
message || `${String(propertyKey)} must be a valid phone number${region ? ` for ${region}` : ''}`,
),
)
}
}

/**
* Checks if the string is a valid IANA timezone identifier.
* @param message - Optional custom error message.
Expand Down
Loading
Loading