@wylan24/mockgen 0.0.0-stage → 0.1.0-alpha.2
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/LICENSE +21 -0
- package/README.md +188 -2
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +278 -0
- package/dist/cli.js.map +1 -0
- package/dist/collections.d.ts +46 -0
- package/dist/collections.js +215 -0
- package/dist/collections.js.map +1 -0
- package/dist/generate.d.ts +19 -0
- package/dist/generate.js +414 -0
- package/dist/generate.js.map +1 -0
- package/dist/heuristics.d.ts +28 -0
- package/dist/heuristics.js +139 -0
- package/dist/heuristics.js.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -0
- package/dist/infer.d.ts +10 -0
- package/dist/infer.js +166 -0
- package/dist/infer.js.map +1 -0
- package/dist/load.d.ts +3 -0
- package/dist/load.js +15 -0
- package/dist/load.js.map +1 -0
- package/dist/model.d.ts +34 -0
- package/dist/model.js +4 -0
- package/dist/model.js.map +1 -0
- package/dist/openapi.d.ts +17 -0
- package/dist/openapi.js +209 -0
- package/dist/openapi.js.map +1 -0
- package/dist/samples.d.ts +14 -0
- package/dist/samples.js +115 -0
- package/dist/samples.js.map +1 -0
- package/dist/schema.d.ts +38 -0
- package/dist/schema.js +321 -0
- package/dist/schema.js.map +1 -0
- package/dist/server.d.ts +45 -0
- package/dist/server.js +175 -0
- package/dist/server.js.map +1 -0
- package/dist/validate.d.ts +21 -0
- package/dist/validate.js +164 -0
- package/dist/validate.js.map +1 -0
- package/package.json +68 -4
package/dist/generate.js
ADDED
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { faker as enFaker } from '@faker-js/faker/locale/en';
|
|
5
|
+
import RandExp from 'randexp';
|
|
6
|
+
import { countMatches, discriminatorOf, fits, mergeAllOf, mergeBranch, typeOf } from './schema.js';
|
|
7
|
+
import { looksLikeDate, looksLikeDateTime, numberByName, stringByName } from './heuristics.js';
|
|
8
|
+
// Faker's date helpers default to "now"; pin them so output never drifts.
|
|
9
|
+
const REF_DATE = new Date('2026-01-01T00:00:00.000Z');
|
|
10
|
+
// The package root loads every locale (~23 MB of RSS); each `locale/<code>` entry only
|
|
11
|
+
// exports a shared instance, so build private ones from its class and definitions.
|
|
12
|
+
// Locales other than English load on first use (~1 MB each).
|
|
13
|
+
const FakerClass = enFaker.constructor;
|
|
14
|
+
const fakers = new Map([['en', privateFaker(enFaker)]]);
|
|
15
|
+
const require = createRequire(import.meta.url);
|
|
16
|
+
let locales;
|
|
17
|
+
function privateFaker(shared) {
|
|
18
|
+
const f = new FakerClass({ locale: shared.rawDefinitions });
|
|
19
|
+
f.setDefaultRefDate(REF_DATE);
|
|
20
|
+
return f;
|
|
21
|
+
}
|
|
22
|
+
/** Locale codes Faker ships, e.g. `de`, `ja`, `pt_BR`. */
|
|
23
|
+
export function availableLocales() {
|
|
24
|
+
locales ??= fs
|
|
25
|
+
.readdirSync(path.dirname(require.resolve('@faker-js/faker/locale/en')))
|
|
26
|
+
.filter((f) => f.endsWith('.js') && f !== 'base.js')
|
|
27
|
+
.map((f) => f.slice(0, -'.js'.length))
|
|
28
|
+
.sort();
|
|
29
|
+
return locales;
|
|
30
|
+
}
|
|
31
|
+
/** Throw a readable error for a locale Faker doesn't ship. */
|
|
32
|
+
export function assertLocale(locale) {
|
|
33
|
+
if (!availableLocales().includes(locale))
|
|
34
|
+
throw new Error(`Unknown locale "${locale}". Available: ${availableLocales().join(', ')}`);
|
|
35
|
+
}
|
|
36
|
+
/** The Faker for `locale`, loaded on first use. */
|
|
37
|
+
function fakerFor(locale = 'en') {
|
|
38
|
+
let f = fakers.get(locale);
|
|
39
|
+
if (!f) {
|
|
40
|
+
assertLocale(locale);
|
|
41
|
+
f = privateFaker(require(`@faker-js/faker/locale/${locale}`).faker);
|
|
42
|
+
fakers.set(locale, f);
|
|
43
|
+
}
|
|
44
|
+
return f;
|
|
45
|
+
}
|
|
46
|
+
/** The Faker for the current `generate` call. Generation is synchronous, so swapping it per call is safe. */
|
|
47
|
+
let faker = fakers.get('en');
|
|
48
|
+
// Filling every optional field of every nested object explodes on big specs (Stripe
|
|
49
|
+
// objects nest dozens of expandable objects). Past this depth, or once this many
|
|
50
|
+
// values have been generated, only required fields and minItems are filled.
|
|
51
|
+
// 250 keeps a Stripe list page of 10 full records near 15 ms (the target is 20 ms
|
|
52
|
+
// p95) while every top-level field of a record is still present.
|
|
53
|
+
const RICH_DEPTH = 3;
|
|
54
|
+
const VALUE_BUDGET = 250;
|
|
55
|
+
/** Generate a realistic, deterministic value that satisfies `schema`. */
|
|
56
|
+
export function generate(schema, opts = {}) {
|
|
57
|
+
faker = fakerFor(opts.locale);
|
|
58
|
+
faker.seed(hash(`${opts.seed ?? 1}:${opts.key ?? ''}`));
|
|
59
|
+
return gen(schema ?? {}, { depth: 0, maxDepth: opts.maxDepth ?? 8, budget: { values: VALUE_BUDGET } });
|
|
60
|
+
}
|
|
61
|
+
function gen(schema, ctx) {
|
|
62
|
+
// Real examples beat fake data, unless the spec's example contradicts its own schema.
|
|
63
|
+
if (schema.example !== undefined && fits(schema.example, schema))
|
|
64
|
+
return structuredClone(schema.example);
|
|
65
|
+
const example = Array.isArray(schema.examples) ? schema.examples.find((e) => fits(e, schema)) : undefined;
|
|
66
|
+
if (example !== undefined)
|
|
67
|
+
return structuredClone(example);
|
|
68
|
+
if ('const' in schema)
|
|
69
|
+
return schema.const;
|
|
70
|
+
if (Array.isArray(schema.enum) && schema.enum.length) {
|
|
71
|
+
// Specs sometimes list enum values their own pattern or maxLength rejects.
|
|
72
|
+
const { enum: values, ...rest } = schema;
|
|
73
|
+
const valid = values.filter((v) => v !== null && fits(v, rest));
|
|
74
|
+
const options = valid.length ? valid : values.filter((v) => v !== null);
|
|
75
|
+
return faker.helpers.arrayElement(options.length ? options : values);
|
|
76
|
+
}
|
|
77
|
+
if (Array.isArray(schema.allOf))
|
|
78
|
+
return gen(mergeAllOf(schema), ctx);
|
|
79
|
+
const choices = schema.oneOf ?? schema.anyOf;
|
|
80
|
+
if (Array.isArray(choices) && choices.length)
|
|
81
|
+
return genChoice(schema, choices, Array.isArray(schema.oneOf), ctx);
|
|
82
|
+
switch (typeOf(schema)) {
|
|
83
|
+
case 'null':
|
|
84
|
+
return null;
|
|
85
|
+
case 'object':
|
|
86
|
+
return genObject(schema, ctx);
|
|
87
|
+
case 'array':
|
|
88
|
+
return genArray(schema, ctx);
|
|
89
|
+
case 'integer':
|
|
90
|
+
return genNumber(schema, ctx, true);
|
|
91
|
+
case 'number':
|
|
92
|
+
return genNumber(schema, ctx, false);
|
|
93
|
+
case 'boolean':
|
|
94
|
+
return faker.datatype.boolean();
|
|
95
|
+
case 'string':
|
|
96
|
+
return genString(schema, ctx);
|
|
97
|
+
default:
|
|
98
|
+
return faker.word.noun();
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Optional fields real APIs only fill when the client asks (HAL's `_embedded` via
|
|
103
|
+
* `?expand=`). Generating them makes every record several times larger than the real
|
|
104
|
+
* thing. Required ones are still filled.
|
|
105
|
+
*/
|
|
106
|
+
const ON_REQUEST = new Set(['_embedded']);
|
|
107
|
+
function lean(ctx) {
|
|
108
|
+
return ctx.minimal === true || ctx.depth > RICH_DEPTH || ctx.budget.values <= 0;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Pick a `oneOf`/`anyOf` branch and generate from it. Branches often overlap (two
|
|
112
|
+
* object shapes with no required fields both accept `{}`), so each candidate is
|
|
113
|
+
* checked: it must satisfy its branch, and for `oneOf` no other branch.
|
|
114
|
+
*/
|
|
115
|
+
function genChoice(schema, choices, exclusive, ctx) {
|
|
116
|
+
const rest = choiceBase(schema);
|
|
117
|
+
const disc = discriminatorOf(schema);
|
|
118
|
+
if (disc)
|
|
119
|
+
return genDiscriminated(rest, disc, ctx);
|
|
120
|
+
// Deep in the tree, prefer the cheapest branch (`string` over an expanded object).
|
|
121
|
+
const order = lean(ctx) ? [...choices].sort((a, b) => cost(a) - cost(b)) : faker.helpers.shuffle([...choices]);
|
|
122
|
+
let fallback;
|
|
123
|
+
// A rejected candidate must not eat the budget of the next one, or later
|
|
124
|
+
// candidates come out bare and match every branch.
|
|
125
|
+
const budget = ctx.budget.values;
|
|
126
|
+
// Second pass: a full object often satisfies a loose sibling branch too, while
|
|
127
|
+
// the bare required fields of the same branch don't.
|
|
128
|
+
for (const attempt of [ctx, { ...ctx, minimal: true }]) {
|
|
129
|
+
for (const branch of order.slice(0, 4)) {
|
|
130
|
+
const s = Object.keys(rest).length ? mergeBranch(rest, branch) : branch;
|
|
131
|
+
ctx.budget.values = budget;
|
|
132
|
+
const value = gen(s, attempt);
|
|
133
|
+
if (fallback === undefined)
|
|
134
|
+
fallback = value;
|
|
135
|
+
if (!fits(value, s))
|
|
136
|
+
continue;
|
|
137
|
+
if (exclusive && choices.length > 1 && countMatches(value, choices, 0, 2) !== 1)
|
|
138
|
+
continue;
|
|
139
|
+
return value;
|
|
140
|
+
}
|
|
141
|
+
if (!exclusive)
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
return fallback;
|
|
145
|
+
}
|
|
146
|
+
/** With a discriminator the branch is chosen by value, and the record carries that value. */
|
|
147
|
+
function genDiscriminated(rest, disc, ctx) {
|
|
148
|
+
const shared = Object.keys(rest).length > 0;
|
|
149
|
+
const branchOf = (m) => (shared ? mergeBranch(rest, m.schema) : m.schema);
|
|
150
|
+
// Skip mapping values the branch's own property schema rejects (a spec bug, but common).
|
|
151
|
+
const usable = disc.mapping.filter((m) => {
|
|
152
|
+
const s = branchOf(m);
|
|
153
|
+
const prop = (s.allOf ? mergeAllOf(s) : s).properties?.[disc.property];
|
|
154
|
+
return !prop || fits(m.value, prop);
|
|
155
|
+
});
|
|
156
|
+
const entry = faker.helpers.arrayElement(usable.length ? usable : disc.mapping);
|
|
157
|
+
const value = gen(branchOf(entry), ctx);
|
|
158
|
+
if (value && typeof value === 'object' && !Array.isArray(value))
|
|
159
|
+
value[disc.property] = entry.value;
|
|
160
|
+
return value;
|
|
161
|
+
}
|
|
162
|
+
// Per-schema derived data, cached by identity: schemas never change, records repeat.
|
|
163
|
+
const baseCache = new WeakMap();
|
|
164
|
+
const siblingCache = new WeakMap();
|
|
165
|
+
/** A oneOf/anyOf schema minus its alternatives: what every branch is merged with. */
|
|
166
|
+
function choiceBase(schema) {
|
|
167
|
+
let base = baseCache.get(schema);
|
|
168
|
+
if (!base) {
|
|
169
|
+
const { oneOf, anyOf, discriminator, ...rest } = schema;
|
|
170
|
+
baseCache.set(schema, (base = rest));
|
|
171
|
+
}
|
|
172
|
+
return base;
|
|
173
|
+
}
|
|
174
|
+
function siblingsOf(props) {
|
|
175
|
+
let set = siblingCache.get(props);
|
|
176
|
+
if (!set)
|
|
177
|
+
siblingCache.set(props, (set = new Set(Object.keys(props).map((k) => k.toLowerCase()))));
|
|
178
|
+
return set;
|
|
179
|
+
}
|
|
180
|
+
function cost(s) {
|
|
181
|
+
if (s.enum || 'const' in s)
|
|
182
|
+
return 0;
|
|
183
|
+
const t = typeOf(s);
|
|
184
|
+
if (t === 'object')
|
|
185
|
+
return 2 + (s.required?.length ?? 0);
|
|
186
|
+
if (t === 'array')
|
|
187
|
+
return 2 + (s.minItems ?? 0);
|
|
188
|
+
if (s.allOf || s.oneOf || s.anyOf)
|
|
189
|
+
return 3;
|
|
190
|
+
return t ? 1 : 3;
|
|
191
|
+
}
|
|
192
|
+
function genObject(schema, ctx) {
|
|
193
|
+
const out = {};
|
|
194
|
+
if (ctx.depth >= ctx.maxDepth)
|
|
195
|
+
return out;
|
|
196
|
+
const props = schema.properties ?? {};
|
|
197
|
+
const required = new Set(schema.required ?? []);
|
|
198
|
+
const record = { siblings: siblingsOf(props), owner: ctx.name };
|
|
199
|
+
for (const [name, propSchema] of Object.entries(props)) {
|
|
200
|
+
if (propSchema.writeOnly)
|
|
201
|
+
continue;
|
|
202
|
+
if (!required.has(name) && (lean(ctx) || ON_REQUEST.has(name)))
|
|
203
|
+
continue;
|
|
204
|
+
ctx.budget.values--;
|
|
205
|
+
out[name] = gen(propSchema, { ...ctx, depth: ctx.depth + 1, name, record });
|
|
206
|
+
}
|
|
207
|
+
// Required keys the schema never describes still have to be present.
|
|
208
|
+
const extra = typeof schema.additionalProperties === 'object' ? schema.additionalProperties : {};
|
|
209
|
+
for (const name of required) {
|
|
210
|
+
if (!(name in out) && !props[name]?.writeOnly)
|
|
211
|
+
out[name] = gen(extra, { ...ctx, depth: ctx.depth + 1, name, record });
|
|
212
|
+
}
|
|
213
|
+
if (!schema.properties && schema.additionalProperties && typeof schema.additionalProperties === 'object' && !lean(ctx)) {
|
|
214
|
+
const count = faker.number.int({ min: 1, max: 3 });
|
|
215
|
+
for (let i = 0; i < count; i++) {
|
|
216
|
+
out[faker.word.noun()] = gen(schema.additionalProperties, { ...ctx, depth: ctx.depth + 1, name: undefined });
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return out;
|
|
220
|
+
}
|
|
221
|
+
function genArray(schema, ctx) {
|
|
222
|
+
const min = schema.minItems ?? 0;
|
|
223
|
+
if (ctx.depth >= ctx.maxDepth)
|
|
224
|
+
return [];
|
|
225
|
+
// Nested arrays multiply (a list of invoices of line items of taxes): keep inner ones short.
|
|
226
|
+
const max = Math.max(min, schema.maxItems ?? Math.max(min, ctx.depth > 1 ? 3 : 5));
|
|
227
|
+
const count = lean(ctx) ? min : faker.number.int({ min: Math.max(min, Math.min(2, max)), max });
|
|
228
|
+
const items = schema.items ?? {};
|
|
229
|
+
// Items of a `users` array are users, so keep the singular name as the hint.
|
|
230
|
+
const name = ctx.name?.replace(/ies$/, 'y').replace(/(ss|x|sh|ch|us)es$/, '$1').replace(/([^s])s$/, '$1');
|
|
231
|
+
const out = [];
|
|
232
|
+
const seen = new Set();
|
|
233
|
+
for (let attempts = 0; out.length < count && attempts < count * 5; attempts++) {
|
|
234
|
+
const value = gen(items, { ...ctx, depth: ctx.depth + 1, name, record: undefined });
|
|
235
|
+
if (schema.uniqueItems) {
|
|
236
|
+
const k = JSON.stringify(value);
|
|
237
|
+
if (seen.has(k))
|
|
238
|
+
continue;
|
|
239
|
+
seen.add(k);
|
|
240
|
+
}
|
|
241
|
+
out.push(value);
|
|
242
|
+
}
|
|
243
|
+
return out;
|
|
244
|
+
}
|
|
245
|
+
function genNumber(schema, ctx, integer) {
|
|
246
|
+
let lo = schema.minimum ?? -Infinity;
|
|
247
|
+
let hi = schema.maximum ?? Infinity;
|
|
248
|
+
// 3.0 uses booleans for exclusive bounds; 3.1 uses numbers.
|
|
249
|
+
const step = integer ? 1 : 0.01;
|
|
250
|
+
if (typeof schema.exclusiveMinimum === 'number')
|
|
251
|
+
lo = Math.max(lo, schema.exclusiveMinimum + step);
|
|
252
|
+
else if (schema.exclusiveMinimum === true)
|
|
253
|
+
lo += step;
|
|
254
|
+
if (typeof schema.exclusiveMaximum === 'number')
|
|
255
|
+
hi = Math.min(hi, schema.exclusiveMaximum - step);
|
|
256
|
+
else if (schema.exclusiveMaximum === true)
|
|
257
|
+
hi -= step;
|
|
258
|
+
const hinted = ctx.name ? numberByName(faker, ctx.name, integer || schema.format === 'int64' || schema.format === 'int32') : undefined;
|
|
259
|
+
let value;
|
|
260
|
+
if (hinted !== undefined && hinted >= lo && hinted <= hi) {
|
|
261
|
+
value = hinted;
|
|
262
|
+
}
|
|
263
|
+
else {
|
|
264
|
+
const min = Number.isFinite(lo) ? lo : Number.isFinite(hi) ? hi - 1000 : 0;
|
|
265
|
+
const max = Number.isFinite(hi) ? hi : min + 1000;
|
|
266
|
+
value = integer
|
|
267
|
+
? faker.number.int({ min: Math.ceil(min), max: Math.floor(max) })
|
|
268
|
+
: faker.number.float({ min, max, fractionDigits: 2 });
|
|
269
|
+
}
|
|
270
|
+
if (schema.multipleOf) {
|
|
271
|
+
const m = schema.multipleOf;
|
|
272
|
+
let snapped = Math.round(value / m) * m;
|
|
273
|
+
if (snapped < lo)
|
|
274
|
+
snapped += m;
|
|
275
|
+
if (snapped > hi)
|
|
276
|
+
snapped -= m;
|
|
277
|
+
value = Number(snapped.toFixed(10));
|
|
278
|
+
}
|
|
279
|
+
if (integer)
|
|
280
|
+
value = Math.round(value);
|
|
281
|
+
return value;
|
|
282
|
+
}
|
|
283
|
+
function genString(schema, ctx) {
|
|
284
|
+
const rec = ctx.record ?? { siblings: new Set() };
|
|
285
|
+
// `avatarUrl: {format: uri}` should still be an avatar, and `email` should match `firstName`.
|
|
286
|
+
const named = ctx.name && NAMED_FORMATS.has(schema.format) ? namedFormat(schema.format, stringByName(faker, ctx.name, rec, schema)) : undefined;
|
|
287
|
+
let value = named ?? byFormat(schema.format) ?? byPattern(schema.pattern);
|
|
288
|
+
if (value === undefined && ctx.name) {
|
|
289
|
+
if (looksLikeDate(ctx.name))
|
|
290
|
+
value = isoDate(faker.date.birthdate());
|
|
291
|
+
else if (looksLikeDateTime(ctx.name))
|
|
292
|
+
value = faker.date.past({ years: 2 }).toISOString();
|
|
293
|
+
else
|
|
294
|
+
value = stringByName(faker, ctx.name, rec, schema);
|
|
295
|
+
}
|
|
296
|
+
value ??= faker.lorem.words({ min: 1, max: 3 });
|
|
297
|
+
if (schema.format || schema.pattern)
|
|
298
|
+
return value;
|
|
299
|
+
const min = schema.minLength ?? 0;
|
|
300
|
+
const max = schema.maxLength ?? Infinity;
|
|
301
|
+
while (value.length < min)
|
|
302
|
+
value += faker.string.alpha({ length: min - value.length, casing: 'lower' });
|
|
303
|
+
if (value.length > max)
|
|
304
|
+
value = value.slice(0, max);
|
|
305
|
+
return value;
|
|
306
|
+
}
|
|
307
|
+
/** Formats where a name-based value (an avatar URL, a matching email) beats a generic one. */
|
|
308
|
+
const NAMED_FORMATS = new Set(['email', 'idn-email', 'uri', 'url', 'iri']);
|
|
309
|
+
/** Keep a name-based value when it also satisfies the generic format. */
|
|
310
|
+
function namedFormat(format, candidate) {
|
|
311
|
+
if (candidate === undefined)
|
|
312
|
+
return undefined;
|
|
313
|
+
if (format === 'email' || format === 'idn-email')
|
|
314
|
+
return /^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(candidate) ? candidate : undefined;
|
|
315
|
+
if (format === 'uri' || format === 'url' || format === 'iri')
|
|
316
|
+
return URL.canParse(candidate) ? candidate : undefined;
|
|
317
|
+
return undefined;
|
|
318
|
+
}
|
|
319
|
+
function byFormat(format) {
|
|
320
|
+
switch (format) {
|
|
321
|
+
case 'email':
|
|
322
|
+
case 'idn-email':
|
|
323
|
+
return faker.internet.email().toLowerCase();
|
|
324
|
+
case 'uri':
|
|
325
|
+
case 'url':
|
|
326
|
+
case 'iri':
|
|
327
|
+
return faker.internet.url();
|
|
328
|
+
case 'uri-reference':
|
|
329
|
+
return `/${faker.lorem.slug(2)}`;
|
|
330
|
+
case 'uuid':
|
|
331
|
+
return faker.string.uuid();
|
|
332
|
+
case 'date':
|
|
333
|
+
return isoDate(faker.date.past({ years: 2 }));
|
|
334
|
+
case 'date-time':
|
|
335
|
+
return faker.date.past({ years: 2 }).toISOString();
|
|
336
|
+
case 'time':
|
|
337
|
+
return `${faker.date.past().toISOString().slice(11, 19)}Z`;
|
|
338
|
+
case 'duration':
|
|
339
|
+
return `PT${faker.number.int({ min: 1, max: 59 })}M`;
|
|
340
|
+
case 'ipv4':
|
|
341
|
+
return faker.internet.ipv4();
|
|
342
|
+
case 'ipv6':
|
|
343
|
+
return faker.internet.ipv6();
|
|
344
|
+
case 'hostname':
|
|
345
|
+
case 'idn-hostname':
|
|
346
|
+
return faker.internet.domainName();
|
|
347
|
+
case 'byte':
|
|
348
|
+
return Buffer.from(faker.lorem.words(3)).toString('base64');
|
|
349
|
+
case 'binary':
|
|
350
|
+
return faker.string.alphanumeric(16);
|
|
351
|
+
case 'password':
|
|
352
|
+
return faker.internet.password({ length: 16 });
|
|
353
|
+
case 'json-pointer':
|
|
354
|
+
return `/${faker.word.noun()}/${faker.number.int(9)}`;
|
|
355
|
+
case 'relative-json-pointer':
|
|
356
|
+
return `0/${faker.word.noun()}`;
|
|
357
|
+
case 'uri-template':
|
|
358
|
+
return `https://${faker.internet.domainName()}/items/{id}`;
|
|
359
|
+
case 'iri-reference':
|
|
360
|
+
return `/${faker.lorem.slug(2)}`;
|
|
361
|
+
case 'regex':
|
|
362
|
+
return '^[a-z0-9-]+$';
|
|
363
|
+
default:
|
|
364
|
+
return undefined;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
function byPattern(pattern) {
|
|
368
|
+
if (!pattern)
|
|
369
|
+
return undefined;
|
|
370
|
+
const compiled = patternGenerator(pattern);
|
|
371
|
+
if (!compiled)
|
|
372
|
+
return undefined;
|
|
373
|
+
for (let i = 0; i < 10; i++) {
|
|
374
|
+
const candidate = compiled.gen.gen();
|
|
375
|
+
if (compiled.re.test(candidate))
|
|
376
|
+
return candidate;
|
|
377
|
+
}
|
|
378
|
+
return undefined;
|
|
379
|
+
}
|
|
380
|
+
// Compiling a RandExp per value dominated pattern-heavy specs; compile once per pattern.
|
|
381
|
+
// Its randomness comes from the shared, reseeded faker, so reuse stays deterministic.
|
|
382
|
+
const patternCache = new Map();
|
|
383
|
+
function patternGenerator(pattern) {
|
|
384
|
+
let compiled = patternCache.get(pattern);
|
|
385
|
+
if (compiled === undefined) {
|
|
386
|
+
try {
|
|
387
|
+
const gen = new RandExp(pattern);
|
|
388
|
+
gen.max = 5; // Cap `*` and `+` so values stay short.
|
|
389
|
+
gen.randInt = (min, max) => faker.number.int({ min, max });
|
|
390
|
+
// Keep generated characters printable ASCII unless the pattern says otherwise.
|
|
391
|
+
gen.defaultRange.subtract(0, 31);
|
|
392
|
+
gen.defaultRange.subtract(127, 65535);
|
|
393
|
+
compiled = { re: new RegExp(pattern, 'u'), gen };
|
|
394
|
+
}
|
|
395
|
+
catch {
|
|
396
|
+
compiled = null; // Unsupported regex features fall through to a plain string.
|
|
397
|
+
}
|
|
398
|
+
patternCache.set(pattern, compiled);
|
|
399
|
+
}
|
|
400
|
+
return compiled;
|
|
401
|
+
}
|
|
402
|
+
function isoDate(d) {
|
|
403
|
+
return d.toISOString().slice(0, 10);
|
|
404
|
+
}
|
|
405
|
+
/** 32-bit FNV-1a; stable across platforms and Node versions. */
|
|
406
|
+
export function hash(input) {
|
|
407
|
+
let h = 0x811c9dc5;
|
|
408
|
+
for (let i = 0; i < input.length; i++) {
|
|
409
|
+
h ^= input.charCodeAt(i);
|
|
410
|
+
h = Math.imul(h, 0x01000193);
|
|
411
|
+
}
|
|
412
|
+
return h >>> 0;
|
|
413
|
+
}
|
|
414
|
+
//# sourceMappingURL=generate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate.js","sourceRoot":"","sources":["../src/generate.ts"],"names":[],"mappings":"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","sourcesContent":["import fs from 'node:fs';\nimport { createRequire } from 'node:module';\nimport path from 'node:path';\nimport type { Faker } from '@faker-js/faker';\nimport { faker as enFaker } from '@faker-js/faker/locale/en';\nimport RandExp from 'randexp';\nimport type { Schema } from './model.js';\nimport { countMatches, type Discriminator, discriminatorOf, fits, mergeAllOf, mergeBranch, typeOf } from './schema.js';\nimport { looksLikeDate, looksLikeDateTime, numberByName, stringByName, type RecordContext } from './heuristics.js';\n\nexport interface GenerateOptions {\n /** Global seed; the same seed + key always yields the same value. */\n seed?: number;\n /** Distinguishes values under one seed, e.g. `GET /users/42 200`. */\n key?: string;\n /** Recursion limit for circular schemas. */\n maxDepth?: number;\n /** Faker locale for names, addresses and text, e.g. `de` or `pt_BR`. Default `en`. */\n locale?: string;\n}\n\n// Faker's date helpers default to \"now\"; pin them so output never drifts.\nconst REF_DATE = new Date('2026-01-01T00:00:00.000Z');\n\n// The package root loads every locale (~23 MB of RSS); each `locale/<code>` entry only\n// exports a shared instance, so build private ones from its class and definitions.\n// Locales other than English load on first use (~1 MB each).\nconst FakerClass = enFaker.constructor as typeof Faker;\nconst fakers = new Map<string, Faker>([['en', privateFaker(enFaker)]]);\nconst require = createRequire(import.meta.url);\nlet locales: string[] | undefined;\n\nfunction privateFaker(shared: Faker): Faker {\n const f = new FakerClass({ locale: shared.rawDefinitions });\n f.setDefaultRefDate(REF_DATE);\n return f;\n}\n\n/** Locale codes Faker ships, e.g. `de`, `ja`, `pt_BR`. */\nexport function availableLocales(): string[] {\n locales ??= fs\n .readdirSync(path.dirname(require.resolve('@faker-js/faker/locale/en')))\n .filter((f) => f.endsWith('.js') && f !== 'base.js')\n .map((f) => f.slice(0, -'.js'.length))\n .sort();\n return locales;\n}\n\n/** Throw a readable error for a locale Faker doesn't ship. */\nexport function assertLocale(locale: string): void {\n if (!availableLocales().includes(locale)) throw new Error(`Unknown locale \"${locale}\". Available: ${availableLocales().join(', ')}`);\n}\n\n/** The Faker for `locale`, loaded on first use. */\nfunction fakerFor(locale = 'en'): Faker {\n let f = fakers.get(locale);\n if (!f) {\n assertLocale(locale);\n f = privateFaker((require(`@faker-js/faker/locale/${locale}`) as { faker: Faker }).faker);\n fakers.set(locale, f);\n }\n return f;\n}\n\n/** The Faker for the current `generate` call. Generation is synchronous, so swapping it per call is safe. */\nlet faker = fakers.get('en')!;\n\n// Filling every optional field of every nested object explodes on big specs (Stripe\n// objects nest dozens of expandable objects). Past this depth, or once this many\n// values have been generated, only required fields and minItems are filled.\n// 250 keeps a Stripe list page of 10 full records near 15 ms (the target is 20 ms\n// p95) while every top-level field of a record is still present.\nconst RICH_DEPTH = 3;\nconst VALUE_BUDGET = 250;\n\ninterface Ctx {\n depth: number;\n maxDepth: number;\n /** Shared across one `generate` call. */\n budget: { values: number };\n /** Only required fields, e.g. to tell apart overlapping `oneOf` branches. */\n minimal?: boolean;\n name?: string;\n record?: RecordContext;\n}\n\n/** Generate a realistic, deterministic value that satisfies `schema`. */\nexport function generate(schema: Schema | undefined, opts: GenerateOptions = {}): unknown {\n faker = fakerFor(opts.locale);\n faker.seed(hash(`${opts.seed ?? 1}:${opts.key ?? ''}`));\n return gen(schema ?? {}, { depth: 0, maxDepth: opts.maxDepth ?? 8, budget: { values: VALUE_BUDGET } });\n}\n\nfunction gen(schema: Schema, ctx: Ctx): unknown {\n // Real examples beat fake data, unless the spec's example contradicts its own schema.\n if (schema.example !== undefined && fits(schema.example, schema)) return structuredClone(schema.example);\n const example = Array.isArray(schema.examples) ? schema.examples.find((e: unknown) => fits(e, schema)) : undefined;\n if (example !== undefined) return structuredClone(example);\n if ('const' in schema) return schema.const;\n if (Array.isArray(schema.enum) && schema.enum.length) {\n // Specs sometimes list enum values their own pattern or maxLength rejects.\n const { enum: values, ...rest } = schema;\n const valid = values.filter((v: unknown) => v !== null && fits(v, rest));\n const options = valid.length ? valid : values.filter((v: unknown) => v !== null);\n return faker.helpers.arrayElement(options.length ? options : values);\n }\n\n if (Array.isArray(schema.allOf)) return gen(mergeAllOf(schema), ctx);\n const choices: Schema[] | undefined = schema.oneOf ?? schema.anyOf;\n if (Array.isArray(choices) && choices.length) return genChoice(schema, choices, Array.isArray(schema.oneOf), ctx);\n\n switch (typeOf(schema)) {\n case 'null':\n return null;\n case 'object':\n return genObject(schema, ctx);\n case 'array':\n return genArray(schema, ctx);\n case 'integer':\n return genNumber(schema, ctx, true);\n case 'number':\n return genNumber(schema, ctx, false);\n case 'boolean':\n return faker.datatype.boolean();\n case 'string':\n return genString(schema, ctx);\n default:\n return faker.word.noun();\n }\n}\n\n/**\n * Optional fields real APIs only fill when the client asks (HAL's `_embedded` via\n * `?expand=`). Generating them makes every record several times larger than the real\n * thing. Required ones are still filled.\n */\nconst ON_REQUEST = new Set(['_embedded']);\n\nfunction lean(ctx: Ctx): boolean {\n return ctx.minimal === true || ctx.depth > RICH_DEPTH || ctx.budget.values <= 0;\n}\n\n/**\n * Pick a `oneOf`/`anyOf` branch and generate from it. Branches often overlap (two\n * object shapes with no required fields both accept `{}`), so each candidate is\n * checked: it must satisfy its branch, and for `oneOf` no other branch.\n */\nfunction genChoice(schema: Schema, choices: Schema[], exclusive: boolean, ctx: Ctx): unknown {\n const rest = choiceBase(schema);\n const disc = discriminatorOf(schema);\n if (disc) return genDiscriminated(rest, disc, ctx);\n // Deep in the tree, prefer the cheapest branch (`string` over an expanded object).\n const order = lean(ctx) ? [...choices].sort((a, b) => cost(a) - cost(b)) : faker.helpers.shuffle([...choices]);\n let fallback: unknown;\n // A rejected candidate must not eat the budget of the next one, or later\n // candidates come out bare and match every branch.\n const budget = ctx.budget.values;\n // Second pass: a full object often satisfies a loose sibling branch too, while\n // the bare required fields of the same branch don't.\n for (const attempt of [ctx, { ...ctx, minimal: true }]) {\n for (const branch of order.slice(0, 4)) {\n const s = Object.keys(rest).length ? mergeBranch(rest, branch) : branch;\n ctx.budget.values = budget;\n const value = gen(s, attempt);\n if (fallback === undefined) fallback = value;\n if (!fits(value, s)) continue;\n if (exclusive && choices.length > 1 && countMatches(value, choices, 0, 2) !== 1) continue;\n return value;\n }\n if (!exclusive) break;\n }\n return fallback;\n}\n\n/** With a discriminator the branch is chosen by value, and the record carries that value. */\nfunction genDiscriminated(rest: Schema, disc: Discriminator, ctx: Ctx): unknown {\n const shared = Object.keys(rest).length > 0;\n const branchOf = (m: Discriminator['mapping'][number]) => (shared ? mergeBranch(rest, m.schema) : m.schema);\n // Skip mapping values the branch's own property schema rejects (a spec bug, but common).\n const usable = disc.mapping.filter((m) => {\n const s = branchOf(m);\n const prop = (s.allOf ? mergeAllOf(s) : s).properties?.[disc.property];\n return !prop || fits(m.value, prop);\n });\n const entry = faker.helpers.arrayElement(usable.length ? usable : disc.mapping);\n const value = gen(branchOf(entry), ctx);\n if (value && typeof value === 'object' && !Array.isArray(value)) (value as Record<string, unknown>)[disc.property] = entry.value;\n return value;\n}\n\n// Per-schema derived data, cached by identity: schemas never change, records repeat.\nconst baseCache = new WeakMap<Schema, Schema>();\nconst siblingCache = new WeakMap<Record<string, Schema>, Set<string>>();\n\n/** A oneOf/anyOf schema minus its alternatives: what every branch is merged with. */\nfunction choiceBase(schema: Schema): Schema {\n let base = baseCache.get(schema);\n if (!base) {\n const { oneOf, anyOf, discriminator, ...rest } = schema;\n baseCache.set(schema, (base = rest));\n }\n return base;\n}\n\nfunction siblingsOf(props: Record<string, Schema>): Set<string> {\n let set = siblingCache.get(props);\n if (!set) siblingCache.set(props, (set = new Set(Object.keys(props).map((k) => k.toLowerCase()))));\n return set;\n}\n\nfunction cost(s: Schema): number {\n if (s.enum || 'const' in s) return 0;\n const t = typeOf(s);\n if (t === 'object') return 2 + (s.required?.length ?? 0);\n if (t === 'array') return 2 + (s.minItems ?? 0);\n if (s.allOf || s.oneOf || s.anyOf) return 3;\n return t ? 1 : 3;\n}\n\nfunction genObject(schema: Schema, ctx: Ctx): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n if (ctx.depth >= ctx.maxDepth) return out;\n\n const props: Record<string, Schema> = schema.properties ?? {};\n const required = new Set<string>(schema.required ?? []);\n const record: RecordContext = { siblings: siblingsOf(props), owner: ctx.name };\n for (const [name, propSchema] of Object.entries(props)) {\n if (propSchema.writeOnly) continue;\n if (!required.has(name) && (lean(ctx) || ON_REQUEST.has(name))) continue;\n ctx.budget.values--;\n out[name] = gen(propSchema, { ...ctx, depth: ctx.depth + 1, name, record });\n }\n\n // Required keys the schema never describes still have to be present.\n const extra = typeof schema.additionalProperties === 'object' ? schema.additionalProperties : {};\n for (const name of required) {\n if (!(name in out) && !props[name]?.writeOnly) out[name] = gen(extra, { ...ctx, depth: ctx.depth + 1, name, record });\n }\n\n if (!schema.properties && schema.additionalProperties && typeof schema.additionalProperties === 'object' && !lean(ctx)) {\n const count = faker.number.int({ min: 1, max: 3 });\n for (let i = 0; i < count; i++) {\n out[faker.word.noun()] = gen(schema.additionalProperties, { ...ctx, depth: ctx.depth + 1, name: undefined });\n }\n }\n return out;\n}\n\nfunction genArray(schema: Schema, ctx: Ctx): unknown[] {\n const min = schema.minItems ?? 0;\n if (ctx.depth >= ctx.maxDepth) return [];\n // Nested arrays multiply (a list of invoices of line items of taxes): keep inner ones short.\n const max = Math.max(min, schema.maxItems ?? Math.max(min, ctx.depth > 1 ? 3 : 5));\n const count = lean(ctx) ? min : faker.number.int({ min: Math.max(min, Math.min(2, max)), max });\n const items: Schema = schema.items ?? {};\n // Items of a `users` array are users, so keep the singular name as the hint.\n const name = ctx.name?.replace(/ies$/, 'y').replace(/(ss|x|sh|ch|us)es$/, '$1').replace(/([^s])s$/, '$1');\n const out: unknown[] = [];\n const seen = new Set<string>();\n for (let attempts = 0; out.length < count && attempts < count * 5; attempts++) {\n const value = gen(items, { ...ctx, depth: ctx.depth + 1, name, record: undefined });\n if (schema.uniqueItems) {\n const k = JSON.stringify(value);\n if (seen.has(k)) continue;\n seen.add(k);\n }\n out.push(value);\n }\n return out;\n}\n\nfunction genNumber(schema: Schema, ctx: Ctx, integer: boolean): number {\n let lo = schema.minimum ?? -Infinity;\n let hi = schema.maximum ?? Infinity;\n // 3.0 uses booleans for exclusive bounds; 3.1 uses numbers.\n const step = integer ? 1 : 0.01;\n if (typeof schema.exclusiveMinimum === 'number') lo = Math.max(lo, schema.exclusiveMinimum + step);\n else if (schema.exclusiveMinimum === true) lo += step;\n if (typeof schema.exclusiveMaximum === 'number') hi = Math.min(hi, schema.exclusiveMaximum - step);\n else if (schema.exclusiveMaximum === true) hi -= step;\n\n const hinted = ctx.name ? numberByName(faker, ctx.name, integer || schema.format === 'int64' || schema.format === 'int32') : undefined;\n let value: number;\n if (hinted !== undefined && hinted >= lo && hinted <= hi) {\n value = hinted;\n } else {\n const min = Number.isFinite(lo) ? lo : Number.isFinite(hi) ? hi - 1000 : 0;\n const max = Number.isFinite(hi) ? hi : min + 1000;\n value = integer\n ? faker.number.int({ min: Math.ceil(min), max: Math.floor(max) })\n : faker.number.float({ min, max, fractionDigits: 2 });\n }\n\n if (schema.multipleOf) {\n const m = schema.multipleOf;\n let snapped = Math.round(value / m) * m;\n if (snapped < lo) snapped += m;\n if (snapped > hi) snapped -= m;\n value = Number(snapped.toFixed(10));\n }\n if (integer) value = Math.round(value);\n return value;\n}\n\nfunction genString(schema: Schema, ctx: Ctx): string {\n const rec = ctx.record ?? { siblings: new Set<string>() };\n // `avatarUrl: {format: uri}` should still be an avatar, and `email` should match `firstName`.\n const named = ctx.name && NAMED_FORMATS.has(schema.format) ? namedFormat(schema.format, stringByName(faker, ctx.name, rec, schema)) : undefined;\n let value = named ?? byFormat(schema.format) ?? byPattern(schema.pattern);\n if (value === undefined && ctx.name) {\n if (looksLikeDate(ctx.name)) value = isoDate(faker.date.birthdate());\n else if (looksLikeDateTime(ctx.name)) value = faker.date.past({ years: 2 }).toISOString();\n else value = stringByName(faker, ctx.name, rec, schema);\n }\n value ??= faker.lorem.words({ min: 1, max: 3 });\n\n if (schema.format || schema.pattern) return value;\n const min = schema.minLength ?? 0;\n const max = schema.maxLength ?? Infinity;\n while (value.length < min) value += faker.string.alpha({ length: min - value.length, casing: 'lower' });\n if (value.length > max) value = value.slice(0, max);\n return value;\n}\n\n/** Formats where a name-based value (an avatar URL, a matching email) beats a generic one. */\nconst NAMED_FORMATS = new Set(['email', 'idn-email', 'uri', 'url', 'iri']);\n\n/** Keep a name-based value when it also satisfies the generic format. */\nfunction namedFormat(format: string | undefined, candidate: string | undefined): string | undefined {\n if (candidate === undefined) return undefined;\n if (format === 'email' || format === 'idn-email') return /^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$/.test(candidate) ? candidate : undefined;\n if (format === 'uri' || format === 'url' || format === 'iri') return URL.canParse(candidate) ? candidate : undefined;\n return undefined;\n}\n\nfunction byFormat(format: string | undefined): string | undefined {\n switch (format) {\n case 'email':\n case 'idn-email':\n return faker.internet.email().toLowerCase();\n case 'uri':\n case 'url':\n case 'iri':\n return faker.internet.url();\n case 'uri-reference':\n return `/${faker.lorem.slug(2)}`;\n case 'uuid':\n return faker.string.uuid();\n case 'date':\n return isoDate(faker.date.past({ years: 2 }));\n case 'date-time':\n return faker.date.past({ years: 2 }).toISOString();\n case 'time':\n return `${faker.date.past().toISOString().slice(11, 19)}Z`;\n case 'duration':\n return `PT${faker.number.int({ min: 1, max: 59 })}M`;\n case 'ipv4':\n return faker.internet.ipv4();\n case 'ipv6':\n return faker.internet.ipv6();\n case 'hostname':\n case 'idn-hostname':\n return faker.internet.domainName();\n case 'byte':\n return Buffer.from(faker.lorem.words(3)).toString('base64');\n case 'binary':\n return faker.string.alphanumeric(16);\n case 'password':\n return faker.internet.password({ length: 16 });\n case 'json-pointer':\n return `/${faker.word.noun()}/${faker.number.int(9)}`;\n case 'relative-json-pointer':\n return `0/${faker.word.noun()}`;\n case 'uri-template':\n return `https://${faker.internet.domainName()}/items/{id}`;\n case 'iri-reference':\n return `/${faker.lorem.slug(2)}`;\n case 'regex':\n return '^[a-z0-9-]+$';\n default:\n return undefined;\n }\n}\n\nfunction byPattern(pattern: string | undefined): string | undefined {\n if (!pattern) return undefined;\n const compiled = patternGenerator(pattern);\n if (!compiled) return undefined;\n for (let i = 0; i < 10; i++) {\n const candidate = compiled.gen.gen();\n if (compiled.re.test(candidate)) return candidate;\n }\n return undefined;\n}\n\n// Compiling a RandExp per value dominated pattern-heavy specs; compile once per pattern.\n// Its randomness comes from the shared, reseeded faker, so reuse stays deterministic.\nconst patternCache = new Map<string, { re: RegExp; gen: RandExp } | null>();\n\nfunction patternGenerator(pattern: string): { re: RegExp; gen: RandExp } | null {\n let compiled = patternCache.get(pattern);\n if (compiled === undefined) {\n try {\n const gen = new RandExp(pattern);\n gen.max = 5; // Cap `*` and `+` so values stay short.\n gen.randInt = (min, max) => faker.number.int({ min, max });\n // Keep generated characters printable ASCII unless the pattern says otherwise.\n gen.defaultRange.subtract(0, 31);\n gen.defaultRange.subtract(127, 65535);\n compiled = { re: new RegExp(pattern, 'u'), gen };\n } catch {\n compiled = null; // Unsupported regex features fall through to a plain string.\n }\n patternCache.set(pattern, compiled);\n }\n return compiled;\n}\n\nfunction isoDate(d: Date): string {\n return d.toISOString().slice(0, 10);\n}\n\n/** 32-bit FNV-1a; stable across platforms and Node versions. */\nexport function hash(input: string): number {\n let h = 0x811c9dc5;\n for (let i = 0; i < input.length; i++) {\n h ^= input.charCodeAt(i);\n h = Math.imul(h, 0x01000193);\n }\n return h >>> 0;\n}\n"]}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Faker } from '@faker-js/faker';
|
|
2
|
+
import type { Schema } from './model.js';
|
|
3
|
+
/** Per-object context so a record's name, username, email and avatar agree with each other. */
|
|
4
|
+
export interface RecordContext {
|
|
5
|
+
/** Property names of the object being generated. */
|
|
6
|
+
siblings: Set<string>;
|
|
7
|
+
/** Name of the property holding this object, e.g. `team` for `team.name`. */
|
|
8
|
+
owner?: string;
|
|
9
|
+
person?: {
|
|
10
|
+
first: string;
|
|
11
|
+
last: string;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
export declare function personOf(faker: Faker, rec: RecordContext): {
|
|
15
|
+
first: string;
|
|
16
|
+
last: string;
|
|
17
|
+
};
|
|
18
|
+
/** Normalize `created_at`, `createdAt`, `Created-At` to `createdat`. */
|
|
19
|
+
export declare function normalizeName(name: string): string;
|
|
20
|
+
/** A realistic string for this property name, or undefined when no rule applies. */
|
|
21
|
+
export declare function stringByName(f: Faker, name: string, rec: RecordContext, schema: Schema): string | undefined;
|
|
22
|
+
/** Whether a string property name implies a timestamp when the schema gives no `format`. */
|
|
23
|
+
export declare function looksLikeDateTime(name: string): boolean;
|
|
24
|
+
/** `id`, `userId`, `user_id`, `uuid` — but not `paid` or `valid`. */
|
|
25
|
+
export declare function isIdName(name: string): boolean;
|
|
26
|
+
export declare function looksLikeDate(name: string): boolean;
|
|
27
|
+
/** A realistic number for this property name, or undefined when no rule applies. */
|
|
28
|
+
export declare function numberByName(f: Faker, name: string, integer: boolean): number | undefined;
|