@@ -20,10 +20,15 @@ finprim is the open source version of what your team has already written three t
2020- ✅ UK sort code and account number validation
2121- ✅ BIC/SWIFT validation
2222- ✅ Card number validation (Luhn, network detection, formatting)
23+ - ✅ EU VAT number format validation (member states)
24+ - ✅ US ABA routing number validation
25+ - ✅ Loan/EMI calculation and schedule
26+ - ✅ Format-only helpers (IBAN, sort code, account number) for display
2327- ✅ Currency validation and formatting with locale support
2428- ✅ Branded types for compile-time correctness
2529- ✅ Zod schemas out of the box
2630- ✅ Optional React hooks for form inputs
31+ - ✅ Optional NestJS validation pipes
2732- ✅ Zero dependencies at the core
2833- ✅ Tree-shakeable ESM and CJS builds
2934- ✅ Fully typed
@@ -56,6 +61,13 @@ import {
5661 validateBIC ,
5762 validateCardNumber ,
5863 validateCurrencyCode ,
64+ validateEUVAT ,
65+ validateUSRoutingNumber ,
66+ formatIBAN ,
67+ formatSortCode ,
68+ formatUKAccountNumber ,
69+ calculateEMI ,
70+ getLoanSchedule ,
5971} from ' finprim'
6072
6173const iban = validateIBAN (' GB29NWBK60161331926819' )
@@ -69,6 +81,19 @@ const account = validateUKAccountNumber('31926819')
6981
7082const card = validateCardNumber (' 4532015112830366' )
7183// { valid: true, value: '...', formatted: '4532 0151 1283 0366', network: 'Visa', last4: '0366' }
84+
85+ const vat = validateEUVAT (' DE123456789' )
86+ // { valid: true, value: 'DE123456789', formatted: 'DE 123456789', countryCode: 'DE' }
87+
88+ const routing = validateUSRoutingNumber (' 021000021' )
89+ // { valid: true, value: '021000021', formatted: '021000021' }
90+
91+ formatIBAN (' GB29NWBK60161331926819' ) // 'GB29 NWBK 6016 1331 9268 19'
92+ formatSortCode (' 601613' ) // '60-16-13'
93+ formatUKAccountNumber (' 31926819' ) // '3192 6819'
94+
95+ const emi = calculateEMI (100_000 , 10 , 12 ) // monthly payment
96+ const schedule = getLoanSchedule (100_000 , 10 , 12 ) // array of { month, payment, principal, interest, balance }
7297```
7398
7499### Currency Formatting
@@ -101,7 +126,7 @@ if (iban.valid) {
101126### Zod Schemas
102127
103128``` ts
104- import { ibanSchema , sortCodeSchema , accountNumberSchema , currencySchema } from ' finprim/zod'
129+ import { ibanSchema , sortCodeSchema , accountNumberSchema , currencySchema , vatSchema , routingNumberSchema } from ' finprim/zod'
105130
106131const PaymentSchema = z .object ({
107132 iban: ibanSchema ,
@@ -132,6 +157,20 @@ function PaymentForm() {
132157}
133158```
134159
160+ ### NestJS Pipes
161+
162+ ``` ts
163+ import { IbanValidationPipe , SortCodeValidationPipe , createValidationPipe } from ' finprim/nest'
164+ import { validateIBAN } from ' finprim'
165+
166+ @Get (' iban/:iban' )
167+ findByIban (@Param (' iban' , IbanValidationPipe ) iban : string ) {
168+ return this .service .findByIban (iban )
169+ }
170+
171+ const MyPipe = createValidationPipe (validateIBAN )
172+ ```
173+
135174---
136175
137176## API Reference
@@ -146,8 +185,25 @@ function PaymentForm() {
146185| ` validateCurrencyCode(input) ` | ` string ` | ` ValidationResult<CurrencyCode> ` |
147186| ` validateBIC(input) ` | ` string ` | ` ValidationResult<BIC> ` |
148187| ` validateCardNumber(input) ` | ` string ` | ` CardValidationResult ` (includes ` network ` , ` last4 ` when valid) |
188+ | ` validateEUVAT(input) ` | ` string ` | ` VATValidationResult ` (includes ` countryCode ` when valid) |
189+ | ` validateUSRoutingNumber(input) ` | ` string ` | ` ValidationResult<RoutingNumber> ` |
149190
150- ### Formatting
191+ ### Formatting & display
192+
193+ | Function | Input | Returns |
194+ | ----------| -------| ---------|
195+ | ` formatIBAN(input) ` | ` string ` | ` string ` (space-separated, no validation) |
196+ | ` formatSortCode(input) ` | ` string ` | ` string ` (XX-XX-XX) |
197+ | ` formatUKAccountNumber(input) ` | ` string ` | ` string ` (XXXX XXXX) |
198+
199+ ### Loan
200+
201+ | Function | Input | Returns |
202+ | ----------| -------| ---------|
203+ | ` calculateEMI(principal, annualRatePercent, months) ` | ` number ` , ` number ` , ` number ` | ` number ` |
204+ | ` getLoanSchedule(principal, annualRatePercent, months) ` | ` number ` , ` number ` , ` number ` | ` LoanScheduleEntry[] ` |
205+
206+ ### Formatting (currency)
151207
152208| Function | Input | Returns |
153209| ----------| -------| ---------|
@@ -165,6 +221,7 @@ Validation results include a `formatted` string when valid (e.g. IBAN and card n
165221| ` finprim ` | Core validators and formatters | none |
166222| ` finprim/zod ` | Zod schemas | ` zod ` |
167223| ` finprim/react ` | React hooks | ` react ` |
224+ | ` finprim/nest ` | NestJS validation pipes | ` @nestjs/common ` |
168225
169226---
170227
@@ -182,8 +239,11 @@ Validation results include a `formatted` string when valid (e.g. IBAN and card n
182239
183240- [x] SWIFT / BIC validation
184241- [x] Luhn algorithm for card number validation
185- - [ ] EU VAT number validation
186- - [ ] NestJS pipe integration
242+ - [x] EU VAT number validation
243+ - [x] NestJS pipe integration
244+ - [x] US routing number validation
245+ - [x] Loan/EMI calculation
246+ - [x] Format-only helpers
187247- [ ] More locale coverage
188248
189249---
@@ -202,6 +262,15 @@ npm run dev
202262
203263---
204264
265+ ## Security
266+
267+ - ** Input length** : All string validators reject input longer than 256 characters to limit memory and CPU use.
268+ - ** Type checking** : Validators require non-empty strings; numeric helpers (e.g. loan/currency) require finite numbers and sane bounds.
269+ - ** No sensitive logging** : The library does not log or persist input; use it in a way that avoids logging full card or account numbers.
270+ - ** Format helpers** : ` formatIBAN ` , ` formatSortCode ` , and ` formatUKAccountNumber ` cap input length and accept only strings to avoid abuse.
271+
272+ ---
273+
205274## License
206275
207276MIT
0 commit comments