# @sampuli/data

> Synthetic, format-true test data for countries worldwide — national IDs, tax numbers, IBANs & SWIFT/BIC, bank accounts, phone numbers and ready-made KYC/transfer/payment scenarios, each correct for its own country. For QA and CI at banks and fintechs anyw

Latest version **0.3.3** (published 2026-09-06) · MIT license · 0 weekly downloads

## Install

```sh
npm install @sampuli/data
pnpm add @sampuli/data
yarn add @sampuli/data
bun add @sampuli/data
```

Provides the command `sampuli`.

## Health

**Score 70/100 (B)** — status: active.

Positive: has types; esm support; no vulnerabilities; recently updated; high maintenance score; high quality score.

Warnings: low downloads; pre 1.0.

## Facts

| | |
|---|---|
| Version | 0.3.3 |
| Published | 2026-09-06 |
| First published | 2026-09-05 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | bundled |
| Module format | ESM + CommonJS |
| Node | >=16 |
| Dependencies | 0 |
| Unpacked size | 1.8 MB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Author | Sampuli |
| Maintainers | kevinbett |
| Keywords | test-data, synthetic-data, mock-data, fake-data, iban, swift-bic, national-id, fintech, banking, qa, ci, faker, kenya, nigeria, africa, kra-pin, bvn, mpesa, sampuli |

## Links

- npm: https://www.npmjs.com/package/@sampuli/data
- Repository: https://github.com/kevinbett/sampuli
- Homepage: https://sampuli.site
- Issues: https://github.com/kevinbett/sampuli/issues
- npm.io page: https://npm.io/package/@sampuli/data

## Alternatives

- [pagerjs](https://npm.io/package/pagerjs.md) — 60 weekly downloads
- [whistle.savefor-mock](https://npm.io/package/whistle.savefor-mock.md) — 4 weekly downloads
- [@crvouga/mockingbird-service-junction](https://npm.io/package/@crvouga/mockingbird-service-junction.md) — 0 weekly downloads
- [@crvouga/mockingbird-service-medplum](https://npm.io/package/@crvouga/mockingbird-service-medplum.md) — 0 weekly downloads
- [@crvouga/mockingbird-service-genebygene](https://npm.io/package/@crvouga/mockingbird-service-genebygene.md) — 0 weekly downloads

## Recent versions

- 0.3.3 (latest) — 2026-09-06
- 0.3.2 — 2026-09-06
- 0.3.1 — 2026-09-05
- 0.3.0 — 2026-09-05
- 0.2.0 — 2026-09-05
- 0.1.0 — 2026-09-05

## README

# @sampuli/data

[![npm version](https://img.shields.io/npm/v/@sampuli/data.svg)](https://www.npmjs.com/package/@sampuli/data)
[![downloads](https://img.shields.io/npm/dm/@sampuli/data.svg)](https://www.npmjs.com/package/@sampuli/data)
[![CI](https://github.com/kevinbett/sampuli/actions/workflows/ci.yml/badge.svg)](https://github.com/kevinbett/sampuli/actions/workflows/ci.yml)
[![license](https://img.shields.io/npm/l/@sampuli/data.svg)](./README.md)
[![zero deps](https://img.shields.io/badge/dependencies-0-brightgreen.svg)](#)

> **Synthetic test data that passes your own validators — 90 countries.**

Most fake-data tools give you `John Doe` and a random 16-digit number. `@sampuli/data`
gives you a **KRA PIN that passes a KRA validator**, a **real bank's SWIFT/BIC**, a
**valid IBAN** (mod-97), an **ICAO-9303 passport MRZ**, a **Luhn-valid card** — each
one shaped to its country's actual rules (checksums, prefixes, bank codes). Entirely
synthetic; corresponds to no real person or account.

```bash
npm install @sampuli/data
```

**▶ Try it in your browser, no install:** [sampuli.site](https://sampuli.site) · or in your terminal: `npx @sampuli/data ke.person`

## Why not faker / Mockaroo?

| | faker.js | Mockaroo | **@sampuli/data** |
|---|:---:|:---:|:---:|
| Country-specific ID / tax formats | ✗ | partial | **✓ 90 countries** |
| Checksum-valid (Luhn, mod-97, mod-11…) | ✗ | ✗ | **✓** |
| Real bank names + SWIFT/BIC | ✗ | ✗ | **✓** |
| Valid IBANs, CLABE, NUBAN… | ✗ | ✗ | **✓** |
| Passport MRZ (ICAO-9303) | ✗ | ✗ | **✓** |
| Coherent records (bank↔SWIFT, gender↔name) | partial | ✗ | **✓** |
| Reproducible seeds · zero deps · free | ✓ | – | **✓** |

Built for **QA and CI at banks and fintechs** — test data that *passes your own
format validation*, so you can exercise KYC, onboarding and payment flows without
touching real PII.

<details><summary>Older intro / disclaimer</summary>

One API, dozens of countries: Kenyan KRA PINs & M-Pesa numbers, Nigerian BVNs,
German & Gulf IBANs, Italian Codice Fiscale, Brazilian CPF/CNPJ, Mexican CLABE,
Turkish TCKN, Singapore NRIC — and many more.

> **Every value is entirely synthetic.** It matches the real *format* only and
> corresponds to no real person, account, or registered number. Never present
> any output as a genuine ID, tax number, IBAN, or bank account.

</details>

## From the terminal (CLI)

No code needed — try it with `npx`:

```bash
npx @sampuli/data ke.person                 # one Kenyan record (JSON)
npx @sampuli/data ke.kra_pin                # a single value
npx @sampuli/data ng.person -n 100          # 100 records
npx @sampuli/data de.person -f name,account,card,passport_mrz
npx @sampuli/data ke.person -n 500 --csv    # CSV to stdout
npx @sampuli/data ke.preset:kyc             # a KYC scenario
npx @sampuli/data --list                    # list all 90 country codes
npx @sampuli/data --list ke                 # a country's fields & presets
```

## Quick start

```js
import { generate, generateMany, listPacks } from '@sampuli/data'

listPacks()             // every installed country, e.g.
                        // ['KE','NG','ZA','EG','DE','FR','IT','TR','SA','BR','MX','SG', …]

// One value — the spec is `country.field`:
generate('ke.phone')    // Kenya     → '0712345678'
generate('ng.bvn')      // Nigeria   → '22803022407'
generate('de.account')  // Germany   → 'DE19100900004650895069'   (IBAN)
generate('it.id')       // Italy     → 'LNEFRC90M22F205G'         (Codice Fiscale)
generate('sg.id')       // Singapore → 'S6763250H'                (NRIC)

// A whole coherent record — any country:
generate('de.person')
// → {
//     name: 'Mia Zimmermann', phone: '0179 1480356', id: 'L0PYL465F',
//     tax: '74259823954', bank: 'HypoVereinsbank', swift: 'HYVEDEMMXXX',
//     account: 'DE68700202706388873556', amount: '€16.680,00'
//   }

generateMany('br.person', 500)   // → 500 coherent Brazilian records (core fields)

// `person` returns the common fields; ask for everything with `full`
// (or an explicit list) — cards, passport + MRZ, transaction, IP, income…:
generate('de.full')                          // every field
generate('de.person', { fields: 'all' })     // same thing
generate('de.person', { fields: ['name', 'card', 'passport_mrz'] })

// Generic types (uuid, enum, int, date, vin, isbn…) — validated where it
// counts (VIN/ISBN/IMEI check digits, RFC-4122 UUIDs):
generate('any.uuid')            // 'f47ac10b-58cc-4372-a567-0e02b2c3d479'
generate('any.enum:free,pro')   // 'pro'
generate('any.vin')             // valid VIN check digit

// Custom schema — mix generic types AND country-accurate fields, à la Mockaroo:
import { generateSchema } from '@sampuli/data'
generateSchema({
  count: 100,
  fields: [
    { name: 'id', type: 'uuid' },
    { name: 'phone', type: 'ke.phone' },     // country-accurate
    { name: 'plan', type: 'enum:free,pro,team' },
    { name: 'mrr', type: 'int:0,500', blank: 0.1 },   // 10% null
  ],
})

// Beyond people — companies and products, correct for the country:
generate('de.company')  // → { name: 'Nova Labs GmbH', industry: 'Software',
                        //     vat: 'DE114...', phone: '0179...', currency: 'EUR', … }
generate('jp.product')  // → { name: 'Pro Speaker', price: 8250, currency: 'JPY',
                        //     ean: '4988... ' (valid EAN-13), sku: 'KT-402917', … }
```

Every pack uses the same spec shape — swap the country code. A pack's fields
and presets vary by country; discover them with `listFields(code)` /
`listPresets(code)`. Records stay internally coherent (bank ↔ SWIFT, gender ↔
name, town ↔ region, email ↔ name).

CommonJS works too:

```js
const { generate } = require('@sampuli/data')
```

## What you can generate

`generate(spec, settings?)` — the `spec` is `pack.selector`:

| Spec | Returns |
|---|---|
| `'ke.phone'`, `'ke.kra_pin'`, `'ke.national_id'`, … | a single field value |
| `'ke.person'` (or just `'ke'`) | a full coherent record (object) |
| `'ke.preset:kyc'` | ordered `[label, value]` rows for a scenario |

Discover what a pack offers:

```js
import { listFields, listPresets } from '@sampuli/data'

listFields('ke')   // [{ key: 'phone', label: 'Phone', num: false, ... }, ...]
listPresets('ke')  // [{ key: 'kyc', name: 'KYC customer profile', ... }, ...]
```

Kenya's field keys, for example: `name`, `phone`, `id`, `kra`, `bank`,
`swift`, `account`, `amount`, `gender`, `dob`, `marital`, `occupation`,
`town`, `county`, `gps`, `address`, `business`, `paybill`, `till`, `plate`,
`email`, `avatar`, `ref`. Presets: `kyc`, `pesalink`, `schoolfees`, `fx`,
`merchant`. Other countries share the common core (name/phone/id/tax/bank/
swift/account/amount) with locale-specific extras and presets — always check
`listFields(code)`.

**Every country also has payment-card and passport fields** (off by default):
`card` (Luhn-valid PAN, scheme-correct BIN), `card_scheme` (Visa / Mastercard /
American Express), `card_expiry` (MM/YY), `card_cvv`, `passport` (number),
`passport_expiry`, and `passport_mrz` — a full ICAO-9303 TD3 machine-readable
zone with correct check digits, coherent with the record's name and gender.

```js
generate('dk.card')          // → '4536 3094 5269 0134'  (Visa, Luhn-valid)
generate('gb.person', { fields: ['name', 'card', 'card_scheme', 'card_cvv', 'passport'] })
generate('za.passport_mrz')  // → two 44-char MRZ lines with valid ICAO check digits
```

**And a set of cross-country extras** (also off by default): a transaction
(`txn_datetime`, `txn_type`, `txn_status`, `txn_channel`, `txn_mcc` with real
ISO-18245 codes), `ip` / `ipv6`, `licence` (driver's licence), `income`
(currency-magnitude-aware for the pack's currency), `employer`, and `country`.

```js
generate('jp.income')   // → '9,970,000 JPY'   (scaled to the local currency)
generate('ke.person', { fields: ['name', 'txn_datetime', 'txn_type', 'txn_mcc', 'ip', 'income'] })
```

## Choosing which fields a record has

`generate('ke.person')` returns the on-by-default columns. To include the
optional PII — email, GPS, date of birth, address, occupation, avatar, plate,
and the rest — pass `fields` (the package equivalent of the app's column
toggles):

```js
generate('ke.person', { fields: 'all' })
// → every field the pack has: name, phone, id, kra, …, gender, dob, gps,
//   address, email, avatar, ref

generate('ke.person', { fields: ['name', 'phone', 'email', 'gps', 'dob'] })
// → { name, phone, email, gps, dob } — exactly those, in that order

generateMany('za.person', 500, { fields: ['name', 'said', 'phone', 'email'] })
```

Records stay coherent whichever fields you pick (e.g. `email` derives from the
generated `name`).

## Settings

```js
generate('ke.phone', { phoneFmt: 'plus' })         // '+254712345678'
generate('ke.phone', { network: 'safaricom' })     // Safaricom prefixes only
generate('ke.kra_pin', { pinType: 'P' })           // company PIN (P…)
generate('ke.amount', { amountStyle: 'number' })   // 12400 (number, not 'KES 12,400.00')
```

Records stay internally coherent: `bank` and `swift` describe the same bank,
`gender` matches the first name, `county` matches the `town`.

## Reproducible output

Pass a `seed` and you get the same data every time — ideal for CI snapshots:

```js
generate('ke.person', { seed: 'test-42' })        // identical on every run
generateMany('ke.person', 100, { seed: 'batch' }) // identical batch every run
```

## Scope

Ships every Sampuli country pack, free, with no volume caps — countries across
Africa, Europe, the Middle East, Asia-Pacific and the Americas, and growing.
Call `listPacks()` for the full set installed, or browse them at
[sampuli.site](https://sampuli.site). Additional packs can be plugged in at
runtime via `registerPack(pack)`.

## Accuracy & synthetic data

Every value is *format-correct*: it matches the real shape and passes standard
format checks. It is **entirely synthetic** — invented at call time and
corresponding to no real person, account, business, or registered number. It is
for testing only; do not present any output as a genuine KRA PIN, national ID,
tax number, IBAN, or bank account.

## License

MIT.

---
_Source: https://npm.io/package/@sampuli/data · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
