@ramiyohay/schema-faker 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +81 -0
- package/dist/core/context.d.ts +4 -0
- package/dist/core/context.js +2 -0
- package/dist/core/deepMerge.d.ts +1 -0
- package/dist/core/deepMerge.js +17 -0
- package/dist/core/random.d.ts +8 -0
- package/dist/core/random.js +23 -0
- package/dist/generate.d.ts +2 -0
- package/dist/generate.js +18 -0
- package/dist/generators/array.d.ts +2 -0
- package/dist/generators/array.js +15 -0
- package/dist/generators/boolean.d.ts +2 -0
- package/dist/generators/boolean.js +6 -0
- package/dist/generators/date.d.ts +2 -0
- package/dist/generators/date.js +33 -0
- package/dist/generators/enum.d.ts +2 -0
- package/dist/generators/enum.js +8 -0
- package/dist/generators/number.d.ts +2 -0
- package/dist/generators/number.js +15 -0
- package/dist/generators/string.d.ts +2 -0
- package/dist/generators/string.js +21 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +5 -0
- package/dist/types.d.ts +5 -0
- package/dist/types.js +2 -0
- package/dist/zod/parseZod.d.ts +2 -0
- package/dist/zod/parseZod.js +58 -0
- package/package.json +35 -0
package/README.md
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# schema-faker
|
|
2
|
+
|
|
3
|
+
Generate fake data from **Zod schemas** in a deterministic, type-safe, and extensible way.
|
|
4
|
+
|
|
5
|
+
`schema-faker` creates realistic-looking mock data **directly from your existing schemas**, without writing manual mocks.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Why schema-faker?
|
|
10
|
+
|
|
11
|
+
Most fake data libraries generate random values field-by-field.
|
|
12
|
+
`schema-faker` works **schema-first**.
|
|
13
|
+
|
|
14
|
+
### Benefits
|
|
15
|
+
- ✅ Uses schemas you already have (Zod)
|
|
16
|
+
- ✅ Deterministic output (seed support)
|
|
17
|
+
- ✅ Fully type-safe
|
|
18
|
+
- ✅ No dependency on Faker
|
|
19
|
+
- ✅ Perfect for tests, seeds, and mock APIs
|
|
20
|
+
|
|
21
|
+
### Type Support
|
|
22
|
+
- ✅ string
|
|
23
|
+
- ✅ number
|
|
24
|
+
- ✅ boolean
|
|
25
|
+
- ✅ object
|
|
26
|
+
- ✅ array
|
|
27
|
+
- ✅ optional
|
|
28
|
+
- ✅ enum
|
|
29
|
+
- ✅ nullable
|
|
30
|
+
- ✅ tuple
|
|
31
|
+
- ✅ union
|
|
32
|
+
- ✅ literal
|
|
33
|
+
- ✅ date
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
## Install
|
|
37
|
+
npm install schema-faker zod
|
|
38
|
+
|
|
39
|
+
## Basic Usage
|
|
40
|
+
```ts
|
|
41
|
+
import { z } from "zod";
|
|
42
|
+
import { generate } from "schema-faker";
|
|
43
|
+
|
|
44
|
+
const User = z.object({
|
|
45
|
+
id: z.string().uuid(),
|
|
46
|
+
email: z.string().email(),
|
|
47
|
+
age: z.number().min(18)
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const user = generate(User);
|
|
51
|
+
|
|
52
|
+
// you can also create multi objects
|
|
53
|
+
|
|
54
|
+
const users = generate(User, {
|
|
55
|
+
count: 5,
|
|
56
|
+
seed: 42,
|
|
57
|
+
});
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
# Override Data Usage
|
|
61
|
+
```ts
|
|
62
|
+
import { z } from "zod";
|
|
63
|
+
import { generate } from "schema-faker";
|
|
64
|
+
|
|
65
|
+
const UserSchema = z.object({
|
|
66
|
+
id: z.string().uuid(),
|
|
67
|
+
email: z.string().email(),
|
|
68
|
+
age: z.number().min(18).max(65),
|
|
69
|
+
isActive: z.boolean(),
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// email and age will stay the same
|
|
73
|
+
const user = generate(UserSchema, {
|
|
74
|
+
overrides: {
|
|
75
|
+
email: "test@example.com",
|
|
76
|
+
age: 30,
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
console.log(user);
|
|
81
|
+
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function deepMerge(target: any, source: any): any;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.deepMerge = deepMerge;
|
|
4
|
+
// Deeply merges two objects
|
|
5
|
+
function deepMerge(target, source) {
|
|
6
|
+
if (source === null || typeof source !== "object" || Array.isArray(source)) {
|
|
7
|
+
return source;
|
|
8
|
+
}
|
|
9
|
+
if (target === null || typeof target !== "object" || Array.isArray(target)) {
|
|
10
|
+
return source;
|
|
11
|
+
}
|
|
12
|
+
const result = { ...target };
|
|
13
|
+
for (const key of Object.keys(source)) {
|
|
14
|
+
result[key] = deepMerge(target[key], source[key]);
|
|
15
|
+
}
|
|
16
|
+
return result;
|
|
17
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Random = void 0;
|
|
4
|
+
class Random {
|
|
5
|
+
constructor(seed = Date.now()) {
|
|
6
|
+
this.seed = seed;
|
|
7
|
+
}
|
|
8
|
+
next() {
|
|
9
|
+
this.seed = (this.seed * 9301 + 49297) % 233280;
|
|
10
|
+
return this.seed / 233280;
|
|
11
|
+
}
|
|
12
|
+
int(min, max) {
|
|
13
|
+
return Math.floor(this.next() * (max - min + 1)) + min;
|
|
14
|
+
}
|
|
15
|
+
bool() {
|
|
16
|
+
return this.next() > 0.5;
|
|
17
|
+
}
|
|
18
|
+
string(length) {
|
|
19
|
+
const chars = "abcdefghijklmnopqrstuvwxyz";
|
|
20
|
+
return Array.from({ length }, () => chars[this.int(0, chars.length - 1)]).join("");
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
exports.Random = Random;
|
package/dist/generate.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generate = generate;
|
|
4
|
+
const random_1 = require("./core/random");
|
|
5
|
+
const parseZod_1 = require("./zod/parseZod");
|
|
6
|
+
const deepMerge_1 = require("./core/deepMerge");
|
|
7
|
+
function generate(schema, options = {}) {
|
|
8
|
+
const ctx = {
|
|
9
|
+
random: new random_1.Random(options.seed),
|
|
10
|
+
};
|
|
11
|
+
// create data based on schema
|
|
12
|
+
const generated = (0, parseZod_1.parseZodSchema)(schema, ctx);
|
|
13
|
+
// apply overrides if provided
|
|
14
|
+
if (options.overrides) {
|
|
15
|
+
return (0, deepMerge_1.deepMerge)(generated, options.overrides);
|
|
16
|
+
}
|
|
17
|
+
return generated;
|
|
18
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generateArray = generateArray;
|
|
4
|
+
const parseZod_1 = require("../zod/parseZod");
|
|
5
|
+
function generateArray(schema, ctx) {
|
|
6
|
+
const min = schema._def.minLength?.value ?? 1;
|
|
7
|
+
const max = schema._def.maxLength?.value ?? min + 4;
|
|
8
|
+
const length = ctx.random.int(min, max);
|
|
9
|
+
const itemSchema = schema._def.type;
|
|
10
|
+
const result = [];
|
|
11
|
+
for (let i = 0; i < length; i++) {
|
|
12
|
+
result.push((0, parseZod_1.parseZodSchema)(itemSchema, ctx));
|
|
13
|
+
}
|
|
14
|
+
return result;
|
|
15
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generateDate = generateDate;
|
|
4
|
+
const DEFAULT_MIN_DATE = new Date("1970-01-01").getTime();
|
|
5
|
+
// Helper to convert Date or number to timestamp
|
|
6
|
+
function toTimestamp(value) {
|
|
7
|
+
if (value instanceof Date) {
|
|
8
|
+
return value.getTime();
|
|
9
|
+
}
|
|
10
|
+
if (typeof value === "number") {
|
|
11
|
+
return value;
|
|
12
|
+
}
|
|
13
|
+
throw new Error("Invalid date constraint value");
|
|
14
|
+
}
|
|
15
|
+
function generateDate(schema, ctx) {
|
|
16
|
+
const checks = schema._def.checks ?? [];
|
|
17
|
+
let min = DEFAULT_MIN_DATE;
|
|
18
|
+
let max = Date.now();
|
|
19
|
+
for (const check of checks) {
|
|
20
|
+
if (check.kind === "min") {
|
|
21
|
+
min = toTimestamp(check.value);
|
|
22
|
+
}
|
|
23
|
+
if (check.kind === "max") {
|
|
24
|
+
max = toTimestamp(check.value);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
// date range validation
|
|
28
|
+
if (min > max) {
|
|
29
|
+
throw new Error("Invalid date range: min > max");
|
|
30
|
+
}
|
|
31
|
+
const timestamp = ctx.random.int(min, max);
|
|
32
|
+
return new Date(timestamp);
|
|
33
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generateEnum = generateEnum;
|
|
4
|
+
function generateEnum(schema, ctx) {
|
|
5
|
+
const values = schema._def.values;
|
|
6
|
+
const index = ctx.random.int(0, values.length - 1);
|
|
7
|
+
return values[index];
|
|
8
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generateNumber = generateNumber;
|
|
4
|
+
function generateNumber(schema, ctx) {
|
|
5
|
+
const checks = schema._def.checks ?? [];
|
|
6
|
+
let min = 0;
|
|
7
|
+
let max = 100;
|
|
8
|
+
for (const check of checks) {
|
|
9
|
+
if (check.kind === "min")
|
|
10
|
+
min = check.value;
|
|
11
|
+
if (check.kind === "max")
|
|
12
|
+
max = check.value;
|
|
13
|
+
}
|
|
14
|
+
return ctx.random.int(min, max);
|
|
15
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generateString = generateString;
|
|
4
|
+
function generateUUID(ctx) {
|
|
5
|
+
const hex = () => ctx.random.int(0, 15).toString(16);
|
|
6
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
7
|
+
const r = ctx.random.int(0, 15);
|
|
8
|
+
const v = c === "x" ? r : (r & 0x3) | 0x8;
|
|
9
|
+
return v.toString(16);
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
function generateString(schema, ctx) {
|
|
13
|
+
const checks = schema._def.checks ?? [];
|
|
14
|
+
if (checks.some((c) => c.kind === "uuid")) {
|
|
15
|
+
return generateUUID(ctx);
|
|
16
|
+
}
|
|
17
|
+
if (checks.some((c) => c.kind === "email")) {
|
|
18
|
+
return `user${ctx.random.int(1, 9999)}@example.com`;
|
|
19
|
+
}
|
|
20
|
+
return ctx.random.string(10);
|
|
21
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { generate } from "./generate";
|
package/dist/index.js
ADDED
package/dist/types.d.ts
ADDED
package/dist/types.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseZodSchema = parseZodSchema;
|
|
4
|
+
const string_1 = require("../generators/string");
|
|
5
|
+
const number_1 = require("../generators/number");
|
|
6
|
+
const boolean_1 = require("../generators/boolean");
|
|
7
|
+
const array_1 = require("../generators/array");
|
|
8
|
+
const enum_1 = require("../generators/enum");
|
|
9
|
+
const date_1 = require("../generators/date");
|
|
10
|
+
function parseZodSchema(schema, ctx) {
|
|
11
|
+
switch (schema._def.typeName) {
|
|
12
|
+
case "ZodString": // String type
|
|
13
|
+
return (0, string_1.generateString)(schema, ctx);
|
|
14
|
+
case "ZodNumber": // Number type
|
|
15
|
+
return (0, number_1.generateNumber)(schema, ctx);
|
|
16
|
+
case "ZodBoolean": // Boolean type
|
|
17
|
+
return (0, boolean_1.generateBoolean)(ctx);
|
|
18
|
+
case "ZodArray": // Array type
|
|
19
|
+
return (0, array_1.generateArray)(schema, ctx);
|
|
20
|
+
case "ZodOptional": {
|
|
21
|
+
// 50% undefined, 50% value
|
|
22
|
+
return ctx.random.bool()
|
|
23
|
+
? undefined
|
|
24
|
+
: parseZodSchema(schema._def.innerType, ctx);
|
|
25
|
+
}
|
|
26
|
+
case "ZodNullable": {
|
|
27
|
+
// 50% null, 50% value
|
|
28
|
+
return ctx.random.bool()
|
|
29
|
+
? null // important: return null here
|
|
30
|
+
: parseZodSchema(schema._def.innerType, ctx);
|
|
31
|
+
}
|
|
32
|
+
case "ZodEnum": // Enum type, return one of the enum values
|
|
33
|
+
return (0, enum_1.generateEnum)(schema, ctx);
|
|
34
|
+
case "ZodObject": // Object type
|
|
35
|
+
const shape = schema.shape;
|
|
36
|
+
const obj = {};
|
|
37
|
+
for (const key in shape) {
|
|
38
|
+
obj[key] = parseZodSchema(shape[key], ctx);
|
|
39
|
+
}
|
|
40
|
+
return obj;
|
|
41
|
+
case "ZodLiteral": // Literal type, always return the literal value
|
|
42
|
+
return schema._def.value;
|
|
43
|
+
case "ZodUnion": {
|
|
44
|
+
// Union type, randomly pick one of the options
|
|
45
|
+
const options = schema._def.options;
|
|
46
|
+
const index = ctx.random.int(0, options.length - 1);
|
|
47
|
+
return parseZodSchema(options[index], ctx);
|
|
48
|
+
}
|
|
49
|
+
case "ZodTuple": { // Tuple type , create array with fixed length and types
|
|
50
|
+
const items = schema._def.items;
|
|
51
|
+
return items.map((itemSchema) => parseZodSchema(itemSchema, ctx));
|
|
52
|
+
}
|
|
53
|
+
case "ZodDate": // Date type
|
|
54
|
+
return (0, date_1.generateDate)(schema, ctx);
|
|
55
|
+
default:
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ramiyohay/schema-faker",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "Generate fake data from Zod schemas in a deterministic way",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsc",
|
|
13
|
+
"test": "vitest"
|
|
14
|
+
},
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "https://github.com/ramiyohay/schema-faker"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"faker",
|
|
21
|
+
"schema",
|
|
22
|
+
"zod",
|
|
23
|
+
"mock",
|
|
24
|
+
"testing",
|
|
25
|
+
"seed",
|
|
26
|
+
"fake-data"
|
|
27
|
+
],
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"zod": "^3.23.0"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"typescript": "^5.9.3",
|
|
33
|
+
"vitest": "^4.0.16"
|
|
34
|
+
}
|
|
35
|
+
}
|