diff --git a/CHANGELOG.md b/CHANGELOG.md index 405263e..f9202c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ All notable changes to this project are documented here. The format follows [Kee ## [Unreleased] +### Added + +- `validatePostalCode` / `isValidPostalCode` for six-digit Romanian postal code format validation + ## [0.1.0] - 2026-10-27 ### Added diff --git a/README.md b/README.md index 086d9e7..570411b 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Try it in your browser

-Validate and decode Romanian identifiers (**CNP**, **CUI/CIF** and **IBAN**) with error messages you can show directly to users. Zero dependencies, fully typed, runs in Node and in the browser. +Validate and decode Romanian identifiers (**CNP**, **CUI/CIF**, **IBAN** and postal codes) with error messages you can show directly to users. Zero dependencies, fully typed, runs in Node and in the browser. ```bash npm install validro @@ -60,7 +60,7 @@ type ValidationResult = | { valid: false; error: string; data: null }; ``` -Only need true or false? Use `isValidCnp`, `isValidCui` and `isValidIbanRo`. +Only need true or false? Use `isValidCnp`, `isValidCui`, `isValidIbanRo` and `isValidPostalCode`. ## API @@ -69,7 +69,8 @@ Only need true or false? Use `isValidCnp`, `isValidCui` and `isValidIbanRo`. | `validateCnp(cnp, now?)` | `ValidationResult`; `now` sets the date used to reject future birth dates | | `validateCui(cui)` | `ValidationResult` | | `validateIbanRo(iban)` | `ValidationResult` | -| `isValidCnp`, `isValidCui`, `isValidIbanRo` | `boolean` | +| `validatePostalCode(postalCode)` | `ValidationResult`; exactly 6 digits | +| `isValidCnp`, `isValidCui`, `isValidIbanRo`, `isValidPostalCode` | `boolean` | | `cnpControlDigit(first12)` | the CNP control digit | | `cuiControlDigit(body)` | the CUI control digit | | `ibanChecksumOk(iban)` | mod-97 check, works for any country | @@ -82,6 +83,7 @@ Only need true or false? Use `isValidCnp`, `isValidCui` and `isValidIbanRo`. | CNP | 13 digits, sex and century digit, real calendar date including leap years, not in the future, county code (01–52, 70), sequence not 000, control digit (key `279146358279`) | | CUI / CIF | optional `RO` prefix, 2–10 digits, no leading zero, control digit (key `753217532`) | | IBAN (RO) | 24 characters: `RO`, 2 check digits, 4-letter bank code, 16 alphanumerics, ISO 13616 mod-97 | +| Postal code | exactly 6 digits | Spaces, dots and dashes in the input are ignored. @@ -101,7 +103,7 @@ All examples in this repository are fictitious: sample CNPs use birth dates in t - [ ] Romanian licence plates - [ ] Phone numbers, normalized to `+40` -- [ ] Postal codes +- [x] Postal codes - [ ] Python port Ideas and bugs are welcome in [issues](https://github.com/Robert-GeorgeDev/ValidRO/issues). See [CONTRIBUTING.md](CONTRIBUTING.md) to get started. diff --git a/src/index.ts b/src/index.ts index 64c5499..6758db9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,5 +4,7 @@ export { validateCui, isValidCui, cuiControlDigit } from "./cui.js"; export type { CuiInfo } from "./cui.js"; export { validateIbanRo, isValidIbanRo, ibanChecksumOk } from "./iban.js"; export type { IbanInfo } from "./iban.js"; +export { validatePostalCode, isValidPostalCode } from "./postal-code.js"; +export type { PostalCodeInfo } from "./postal-code.js"; export { CNP_COUNTIES } from "./counties.js"; export type { ValidationResult } from "./result.js"; diff --git a/src/postal-code.ts b/src/postal-code.ts new file mode 100644 index 0000000..b46ec52 --- /dev/null +++ b/src/postal-code.ts @@ -0,0 +1,15 @@ +import { clean, fail, ok, type ValidationResult } from "./result.js"; + +export interface PostalCodeInfo { + postalCode: string; +} + +/** Validates the six-digit format of a Romanian postal code. Spaces, dots and dashes are ignored. */ +export function validatePostalCode(input: string): ValidationResult { + if (typeof input !== "string") return fail("Codul poștal trebuie să fie un șir de caractere"); + const postalCode = clean(input); + if (!/^\d{6}$/.test(postalCode)) return fail("Codul poștal trebuie să conțină exact 6 cifre"); + return ok({ postalCode }); +} + +export const isValidPostalCode = (input: string): boolean => validatePostalCode(input).valid; diff --git a/test/postal-code.test.ts b/test/postal-code.test.ts new file mode 100644 index 0000000..5a5d9bd --- /dev/null +++ b/test/postal-code.test.ts @@ -0,0 +1,19 @@ +import { describe, expect, it } from "vitest"; +import { isValidPostalCode, validatePostalCode } from "../src/postal-code.js"; + +describe("Romanian postal code", () => { + it("accepts exactly six digits and rejects other formats", () => { + expect(validatePostalCode("010101")).toEqual({ valid: true, error: null, data: { postalCode: "010101" } }); + expect(isValidPostalCode("12345")).toBe(false); + expect(isValidPostalCode("1234567")).toBe(false); + expect(isValidPostalCode("12345a")).toBe(false); + }); + + it("ignores spaces, like the other validators", () => { + expect(validatePostalCode(" 010 101 ").data).toEqual({ postalCode: "010101" }); + }); + + it("explains what is wrong", () => { + expect(validatePostalCode("1234").error).toContain("exact 6 cifre"); + }); +});