npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@sampuli/data

v0.3.3

Published

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

Readme

@sampuli/data

npm version downloads CI license zero deps

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.

npm install @sampuli/data

▶ Try it in your browser, no install: 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.

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.

From the terminal (CLI)

No code needed — try it with npx:

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

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:

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:

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.

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.

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):

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

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:

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. 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.