A modern, modular, tree-shakable, zero-runtime-dependency fake data generator for TypeScript and JavaScript.
A production-ready alternative to Faker.js — with React integration and a CLI built in.
Website · Try it live · Docs · npm · GitHub
- 100+ generators across 16 modules (
forge.listGenerators()to see every one) - Zero runtime dependencies
- ESM + CommonJS, one package
- Works in Node.js, Bun, Deno, and browsers — no runtime-specific APIs, only web-standard globals (
btoa,crypto-free PRNG) - 100% typed, every generator has a precise return type
- Tree-shakable — import only the modules/functions you use
- Seedable — deterministic output for tests and snapshots
- 9 built-in locales, plus a custom locale API
- Custom generators via
forge.define(...) - Schema-based generation (
@sahinur/forgedata/zod): fake data straight from a Zod schema - React integration (
@sahinur/forgedata/react): a provider + two hooks - CLI (
forgedata): generate fake data from your terminal or scripts - 100% test coverage (statements/branches/functions/lines)
- Install
- Quick start
- Deterministic output (seeding)
- Locales
- Custom generators
- Unique values
- Helpers & randomness utilities
- Generator introspection
- Zod schema generation
- React integration
- CLI
- Modules
- Runtime support
- Development
- Website
- Publishing
- License
npm install @sahinur/forgedataimport { ForgeData } from "@sahinur/forgedata";
const forge = new ForgeData();
forge.person.fullName(); // "Ava Thompson"
forge.internet.email(); // "ava.thompson@example.com"
forge.location.country(); // "Canada"
forge.finance.creditCardNumber(); // "4539123456781234"
forge.phone.number(); // "+1-555-123-4567"
forge.ai.prompt(); // "Explain quantum computing to a beginner..."Or use the ready-made default instance for quick scripts:
import { forge } from "@sahinur/forgedata";
console.log(forge.person.fullName());const forge = new ForgeData({ seed: 42 });
forge.person.fullName(); // always the same value for seed 42
forge.seed(42); // reseed an existing instanceSeeds accept a number or a string (strings are hashed into a seed), so you can seed with something readable like a test name.
const forge = new ForgeData({ locale: "ja" });
forge.person.fullName(); // Japanese name pool
forge.locale("fr"); // switch at runtime
forge.locale(); // "fr" — read the current localeBuilt-in locales: en, bn (Bangla), hi (Hindi), ar (Arabic), ja (Japanese), fr (French), de (German), es (Spanish), zh (Chinese).
forge.defineLocale({
code: "pirate",
name: "Pirate",
person: {
firstNamesMale: ["Blackbeard", "Redbeard"],
firstNamesFemale: ["Anne", "Mary"],
lastNames: ["Silver", "Flint"],
jobTitles: ["First Mate"],
professions: ["Sailor"],
},
location: { cities: ["Tortuga"], states: ["Caribbean"], countries: ["Never Land"] },
company: { suffixes: ["& Co"], catchPhraseAdjectives: ["Salty"], catchPhraseNouns: ["treasure"] },
lorem: { words: ["arr", "matey", "doubloon"] },
});
forge.locale("pirate");forge.define("pokemon", (f) => f.pick(["Pikachu", "Bulbasaur", "Charmander"]));
forge.custom.pokemon(); // "Pikachu"forge.unique.email(); // never repeats for this ForgeData instance
forge.unique.username();
forge.unique.uuid();
const uniqueCity = forge.unique.wrap(() => forge.location.city());
uniqueCity();
forge.unique.clear(); // forget everything generated so farforge.uuid();
forge.shuffle([1, 2, 3]);
forge.pick(["a", "b", "c"]);
forge.pickMultiple(["a", "b", "c", "d"], 2);
forge.randomEnum(MyEnum);
forge.probability(0.3);
forge.weighted([
{ weight: 9, value: "common" },
{ weight: 1, value: "rare" },
]);
forge.randomArray(() => forge.person.fullName(), 5);Every module is walked via prototype introspection rather than a hand-maintained registry, so this never drifts from the real API:
forge.listGenerators();
// [{ module: "person", method: "fullName", id: "person.fullName" }, ...] (100+ entries)
forge.invoke("person", "fullName"); // same as forge.person.fullName()This is what the CLI is built on.
npm install @sahinur/forgedata zodimport { z } from "zod";
import { fromZodSchema } from "@sahinur/forgedata/zod";
const UserSchema = z.object({
id: z.string().uuid(),
name: z.string(),
email: z.string().email(),
age: z.number().int().min(18).max(99),
role: z.enum(["admin", "user", "guest"]),
tags: z.array(z.string()).min(1).max(3),
bio: z.string().max(140).optional(),
});
const user = fromZodSchema(forge, UserSchema);
// fully typed as z.infer<typeof UserSchema> — every field respects the
// schema's constraints: a valid uuid, a real-looking email, age in [18, 99],
// one of the three enum values, 1-3 tags, bio present ~80% of the time.Supports the full range of Zod v4 schema types recursively: primitives (with format-aware strings — .email(), .url(), .uuid(), .datetime(), .ipv4(), .jwt(), and more — plus .min()/.max()/.length()), object, array, tuple, record, map, set, enum/native enums, literal, union/discriminated union, optional/nullable/default/catch, and recursive schemas via z.lazy() (depth-capped, so a self-referential schema can't blow the stack).
.transform()/.pipe() are run through the schema's real safeParse() after generation, so the output reflects the actual post-transform shape. Arbitrary .refine() predicates aren't evaluated ahead of time — a generated value can occasionally fail one, in which case the raw (pre-refine) value is returned rather than throwing. zod is an optional peer dependency (^4.0.0); the main @sahinur/forgedata entry point never imports it, exactly like the React integration.
import { ForgeDataProvider, useForgeData, useGenerator } from "@sahinur/forgedata/react";
function App() {
return (
<ForgeDataProvider seed={42} locale="en">
<UserCard />
</ForgeDataProvider>
);
}
function UserCard() {
// Regenerated only when `deps` changes — stable across re-renders otherwise.
const user = useGenerator(
(forge) => ({ name: forge.person.fullName(), email: forge.internet.email() }),
[],
);
return <div>{user.name} — {user.email}</div>;
}useForgeData() works even without a ForgeDataProvider (it falls back to a shared default instance) — wrap your tree in a provider whenever you need a specific seed/locale or per-instance isolation. react is an optional peer dependency; the main @sahinur/forgedata entry point never imports it.
npx --package=@sahinur/forgedata forgedata list # every generator id
npx --package=@sahinur/forgedata forgedata list --module person # filtered to one module
npx --package=@sahinur/forgedata forgedata generate person.fullName # one value
npx --package=@sahinur/forgedata forgedata generate internet.email --count 5 --seed 42
npx --package=@sahinur/forgedata forgedata generate location.country --locale ja --jsonInstall it globally (npm install -g @sahinur/forgedata) to just run forgedata ... directly; see the docs site for the full flag reference.
| Module | Examples |
|---|---|
person |
firstName(), lastName(), fullName(), gender(), jobTitle(), profession() |
internet |
email(), username(), password(), url(), domain(), ipv4(), ipv6(), mac(), userAgent(), jwt(), apiKey(), jwtSecret() |
company |
name(), catchPhrase(), logo() |
finance |
currency(), amount(), creditCardNumber(), creditCardCVV(), iban(), bitcoinAddress(), ethereumAddress(), cryptoCoin(), stockSymbol() |
location |
country(), state(), city(), zipCode(), latitude(), longitude(), timezone(), airport() |
commerce |
productName(), price(), department(), isbn(), barcode() |
phone |
number(), imei() |
date |
past(), future(), recent(), birthdate(), month(), weekday(), between() |
image |
avatar(), url(), category(), dataUri() |
lorem |
word(), words(), sentence(), paragraph(), slug(), markdown(), html() |
color |
hex(), rgb(), rgba(), hsl(), cssColorName(), tailwindColor(), materialColor(), svgPattern() |
vehicle |
manufacturer(), model(), type(), vin() |
animal |
type(), name() |
science |
unit(), chemicalElement() |
ai |
prompt(), chatConversation(), codeSnippet(), sqlQuery(), json(), markdown(), apiResponse(), logLine(), commitMessage(), issueTitle(), prDescription() |
misc |
emoji(), hashtag(), programmingLanguage(), githubUsername(), gitCommitHash(), semver(), dockerImageName(), npmPackageName(), otp(), licenseKey(), qrData(), movie(), book(), music(), food(), holiday(), university() |
Run forge.listGenerators().length to see the exact live count (100+).
ForgeData avoids Node-only APIs in src/ — it uses only btoa, Math/Date, and standard JS, all of which exist in Node 18+, Bun, Deno, and every modern browser. It's published as ESM + CJS so:
- Node.js:
import/requireboth work out of the box. - Bun: works via the same ESM/CJS builds.
- Deno:
import { ForgeData } from "npm:@sahinur/forgedata"; - Browsers:
import { ForgeData } from "@sahinur/forgedata";through any bundler, orexamples/browser.htmlfor a zero-bundler example.
npm install
npm run build # tsup -> dist (ESM + CJS + .d.ts, for index/react/zod/cli)
npm run test:coverage # vitest, enforces 100% coverage thresholds
npm run lint
npm run bench # micro-benchmark, compares against faker if installed
npm run cli -- list # run the CLI from source during developmentSee the documentation site (built from docs/docs.html) for the getting-started guide, full API reference, React guide, CLI reference, migration guide from Faker.js, and contributing guide. See examples/ for runnable Node, browser, React, and CLI samples.
docs/ is a plain HTML + Tailwind CSS landing page and single-page documentation site (no framework, no server-side build):
cd docs
npm install
npm run build # compiles src/input.css -> dist/output.css (commit the result)
npm run serve # or just open index.html directlyDeployed straight from the repo — no CI needed: Settings → Pages → Source → Deploy from a branch → main / docs. Because there's no build step in that flow, docs/dist/output.css is committed rather than gitignored; run npm run build in docs/ and commit the output whenever you touch docs/src/input.css or add new Tailwind classes to the HTML.
This repo is fully set up to publish, but nothing publishes itself:
npm run preflight # lint + typecheck + 100%-coverage tests + build
npm login
npm publish --access publicscripts/publish.sh wraps that sequence with a confirmation prompt, and RELEASE_NOTES.md has a ready-to-paste description for the GitHub Release / npm announcement.
For every release after the first, .github/workflows/release.yml runs semantic-release on pushes to main — it derives the next version from Conventional Commits, updates CHANGELOG.md, tags, and publishes, but only once you add an NPM_TOKEN repository secret. Until then it's a no-op (see the docs site's Contributing section); it never requires a token to just sit there ready.
MIT