@wylan24/mockgen 0.1.0-alpha.1 → 0.1.0-alpha.4
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 +13 -2
- package/dist/cli.js +81 -29
- package/dist/cli.js.map +1 -1
- package/dist/collections.d.ts +1 -0
- package/dist/collections.js +7 -7
- package/dist/collections.js.map +1 -1
- package/dist/generate.d.ts +6 -0
- package/dist/generate.js +41 -4
- package/dist/generate.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/server.d.ts +4 -0
- package/dist/server.js +7 -3
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -53,6 +53,7 @@ mockgen infer <dir> [-o openapi.yaml] # Write the schema inferred from samples
|
|
|
53
53
|
| `-s, --seed` | `1` | Seed for generated data |
|
|
54
54
|
| `-d, --delay` | `0` | Delay every response, e.g. `300ms` |
|
|
55
55
|
| `-r, --records` | `50` | Records in every list endpoint's collection |
|
|
56
|
+
| `-l, --locale` | `en` | Language and region for names, addresses and phone numbers, e.g. `de`, `ja`, `pt_BR` |
|
|
56
57
|
| `--no-watch` | | Disable hot reload |
|
|
57
58
|
| `-o, --out` | stdout | `infer` output file (`.yaml` or `.json`) |
|
|
58
59
|
|
|
@@ -66,6 +67,16 @@ curl 'localhost:4010/users/1?__status=503' # undocumented codes work
|
|
|
66
67
|
curl -H 'X-Mock-Delay: 1500' localhost:4010/users # slow this one down
|
|
67
68
|
```
|
|
68
69
|
|
|
70
|
+
### Other languages
|
|
71
|
+
|
|
72
|
+
Names, addresses, companies and phone numbers follow `--locale`. It takes any of the 76 locales [Faker ships](https://fakerjs.dev/guide/localization.html#available-locales), such as `de`, `fr`, `ja`, `pt_BR` or `zh_CN`:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
mockgen serve ./openapi.yaml --locale de
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
The output stays deterministic per locale, and emails and usernames stay valid ASCII. Free text like `description` and `bio` is Latin placeholder text in every locale.
|
|
79
|
+
|
|
69
80
|
### Lists and pagination
|
|
70
81
|
|
|
71
82
|
Every list endpoint — a bare array of objects, or an object wrapping one (`data`, `items`, `results`, …) — is a window onto a stable collection of 50 records (`--records`):
|
|
@@ -87,7 +98,7 @@ A page past the end is empty, even if the schema sets `minItems`.
|
|
|
87
98
|
```ts
|
|
88
99
|
import { createMockServer } from '@wylan24/mockgen';
|
|
89
100
|
|
|
90
|
-
const mock = await createMockServer({ spec: './openapi.yaml', seed: 1 });
|
|
101
|
+
const mock = await createMockServer({ spec: './openapi.yaml', seed: 1, locale: 'en' });
|
|
91
102
|
const url = await mock.listen(0); // random free port
|
|
92
103
|
// ... run tests against url ...
|
|
93
104
|
await mock.close();
|
|
@@ -165,7 +176,7 @@ Bump `version` in `package.json`, commit, then push a matching tag:
|
|
|
165
176
|
git tag v0.1.0-alpha.1 && git push origin v0.1.0-alpha.1
|
|
166
177
|
```
|
|
167
178
|
|
|
168
|
-
The [release workflow](.github/workflows/release.yml) reruns the tests, smoke test and strict corpus gate, publishes to npm with provenance
|
|
179
|
+
The [release workflow](.github/workflows/release.yml) reruns the tests, smoke test and strict corpus gate, publishes to npm with provenance, and creates the GitHub release. Pre-releases go under the `next` dist-tag, and also under `latest` until a stable version exists. To point a dist-tag at a published version later (for example, to roll `latest` back), run the workflow manually from the Actions tab. It publishes through npm trusted publishing, so no npm token is stored anywhere. The workflow file is registered with npm by name, so don't rename it.
|
|
169
180
|
|
|
170
181
|
## Contributing
|
|
171
182
|
|
package/dist/cli.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import fs from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { parseArgs, styleText } from 'node:util';
|
|
5
|
-
import { generate } from './generate.js';
|
|
5
|
+
import { assertLocale, generate } from './generate.js';
|
|
6
6
|
import { loadSource } from './load.js';
|
|
7
7
|
import { formatOpenApi, loadSamples } from './samples.js';
|
|
8
8
|
import { createMockServer, respond } from './server.js';
|
|
@@ -24,6 +24,8 @@ Options
|
|
|
24
24
|
-s, --seed <n> Seed for generated data (default 1)
|
|
25
25
|
-d, --delay <ms> Delay every response, e.g. 300 or 300ms
|
|
26
26
|
-r, --records <n> Records per list endpoint, paged with ?page=&limit= (default 50)
|
|
27
|
+
-l, --locale <code> Language and region for names, addresses and text, e.g. de, ja,
|
|
28
|
+
pt_BR (default en)
|
|
27
29
|
-o, --out <file> infer: write here instead of stdout (.yaml or .json)
|
|
28
30
|
--no-watch Disable hot reload
|
|
29
31
|
-h, --help Show this help
|
|
@@ -46,6 +48,7 @@ async function main(argv) {
|
|
|
46
48
|
delay: { type: 'string', short: 'd', default: '0' },
|
|
47
49
|
out: { type: 'string', short: 'o' },
|
|
48
50
|
records: { type: 'string', short: 'r', default: '50' },
|
|
51
|
+
locale: { type: 'string', short: 'l' },
|
|
49
52
|
watch: { type: 'boolean', default: true },
|
|
50
53
|
help: { type: 'boolean', short: 'h' },
|
|
51
54
|
version: { type: 'boolean', short: 'v' },
|
|
@@ -59,13 +62,22 @@ async function main(argv) {
|
|
|
59
62
|
if (!spec)
|
|
60
63
|
fail(`Missing <spec>. Try: mockgen ${command} ./openapi.yaml`);
|
|
61
64
|
const seed = int(values.seed, '--seed');
|
|
65
|
+
const locale = values.locale;
|
|
66
|
+
if (locale) {
|
|
67
|
+
try {
|
|
68
|
+
assertLocale(locale);
|
|
69
|
+
}
|
|
70
|
+
catch (err) {
|
|
71
|
+
fail(err.message);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
62
74
|
switch (command) {
|
|
63
75
|
case 'serve':
|
|
64
|
-
return serve(spec, { port: int(values.port, '--port'), host: values.host, seed, delay: int(values.delay.replace(/ms$/, ''), '--delay'), watch: values.watch, records: int(values.records, '--records') });
|
|
76
|
+
return serve(spec, { port: int(values.port, '--port'), host: values.host, seed, delay: int(values.delay.replace(/ms$/, ''), '--delay'), watch: values.watch, records: int(values.records, '--records'), locale });
|
|
65
77
|
case 'generate':
|
|
66
|
-
return generateCmd(spec, rest.join(' '), seed, int(values.records, '--records'));
|
|
78
|
+
return generateCmd(spec, rest.join(' '), seed, int(values.records, '--records'), locale);
|
|
67
79
|
case 'validate':
|
|
68
|
-
return validate(spec, seed);
|
|
80
|
+
return validate(spec, seed, locale);
|
|
69
81
|
case 'infer':
|
|
70
82
|
return infer(spec, values.out);
|
|
71
83
|
default:
|
|
@@ -76,7 +88,7 @@ async function serve(spec, opts) {
|
|
|
76
88
|
const started = performance.now();
|
|
77
89
|
const model = await loadSource(spec);
|
|
78
90
|
printWarnings(model);
|
|
79
|
-
const mock = createMockServer({ model, seed: opts.seed, delay: opts.delay, records: opts.records, log: (line) => console.log(styleText('dim', line)) });
|
|
91
|
+
const mock = createMockServer({ model, seed: opts.seed, delay: opts.delay, records: opts.records, locale: opts.locale, log: (line) => console.log(styleText('dim', line)) });
|
|
80
92
|
const url = await mock.listen(opts.port, opts.host).catch((err) => {
|
|
81
93
|
if (err.code === 'EADDRINUSE')
|
|
82
94
|
fail(`Port ${opts.port} is already in use on ${opts.host}. Pick another with --port ${opts.port + 1}, or --port 0 for any free port.`);
|
|
@@ -109,48 +121,88 @@ async function serve(spec, opts) {
|
|
|
109
121
|
process.once('SIGTERM', shutdown);
|
|
110
122
|
}
|
|
111
123
|
/**
|
|
112
|
-
* Watch
|
|
124
|
+
* Watch the spec's files and reload on change, debounced.
|
|
113
125
|
*
|
|
114
|
-
*
|
|
115
|
-
* a watcher
|
|
116
|
-
* rename it over the original)
|
|
126
|
+
* Each file is watched through its folder, filtered by name, rather than directly:
|
|
127
|
+
* on Linux and macOS a watcher on a file follows that file, so an editor's save
|
|
128
|
+
* (write a temp file, rename it over the original) would leave it watching a file
|
|
129
|
+
* that no longer exists. A folder watcher sees the rename and keeps working.
|
|
130
|
+
* Sample folders are watched recursively, and any change in them counts.
|
|
117
131
|
*/
|
|
118
132
|
function watchSources(model, reload) {
|
|
119
|
-
|
|
120
|
-
|
|
133
|
+
// folder → names of the spec files in it, or 'all' for a sample folder
|
|
134
|
+
const watchers = new Map();
|
|
121
135
|
let timer;
|
|
122
136
|
let retry;
|
|
123
137
|
const onChange = () => {
|
|
124
138
|
clearTimeout(timer);
|
|
125
139
|
timer = setTimeout(async () => {
|
|
126
|
-
|
|
127
|
-
|
|
140
|
+
const next = await reload();
|
|
141
|
+
if (next)
|
|
142
|
+
watch(next.sources);
|
|
128
143
|
}, 100);
|
|
129
144
|
};
|
|
130
|
-
const watch = () => {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
145
|
+
const watch = (sources) => {
|
|
146
|
+
const wanted = new Map();
|
|
147
|
+
for (const source of sources) {
|
|
148
|
+
const isDir = fs.statSync(source, { throwIfNoEntry: false })?.isDirectory();
|
|
149
|
+
if (isDir) {
|
|
150
|
+
wanted.set(path.resolve(source), 'all');
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
const dir = path.dirname(path.resolve(source));
|
|
154
|
+
const names = wanted.get(dir);
|
|
155
|
+
if (names !== 'all')
|
|
156
|
+
wanted.set(dir, (names ?? new Set()).add(path.basename(source)));
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
for (const [dir, entry] of watchers) {
|
|
160
|
+
if (!wanted.has(dir)) {
|
|
161
|
+
entry.watcher.close();
|
|
162
|
+
watchers.delete(dir);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
let failed = false;
|
|
166
|
+
for (const [dir, names] of wanted) {
|
|
167
|
+
const existing = watchers.get(dir);
|
|
168
|
+
if (existing && (existing.names === 'all') === (names === 'all')) {
|
|
169
|
+
existing.names = names;
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
existing?.watcher.close();
|
|
136
173
|
try {
|
|
137
|
-
|
|
174
|
+
const watcher = fs.watch(dir, { recursive: names === 'all' }, (_event, filename) => {
|
|
175
|
+
const current = watchers.get(dir)?.names;
|
|
176
|
+
// Some platforms omit the file name; then any change in the folder counts.
|
|
177
|
+
if (current === 'all' || !filename || current?.has(path.basename(filename.toString())))
|
|
178
|
+
onChange();
|
|
179
|
+
});
|
|
180
|
+
// A watcher error (e.g. the folder was deleted) would otherwise crash the server.
|
|
181
|
+
watcher.on('error', () => {
|
|
182
|
+
watcher.close();
|
|
183
|
+
watchers.delete(dir);
|
|
184
|
+
clearTimeout(retry);
|
|
185
|
+
retry = setTimeout(() => watch(sources), 500);
|
|
186
|
+
});
|
|
187
|
+
watchers.set(dir, { watcher, names });
|
|
138
188
|
}
|
|
139
189
|
catch {
|
|
140
|
-
|
|
190
|
+
failed = true; // e.g. the folder doesn't exist right now
|
|
141
191
|
}
|
|
142
192
|
}
|
|
143
|
-
if (
|
|
144
|
-
retry
|
|
193
|
+
if (failed) {
|
|
194
|
+
clearTimeout(retry);
|
|
195
|
+
retry = setTimeout(() => watch(sources), 500);
|
|
196
|
+
}
|
|
145
197
|
};
|
|
146
|
-
watch();
|
|
198
|
+
watch(model.sources);
|
|
147
199
|
}
|
|
148
|
-
async function generateCmd(spec, route, seed, records) {
|
|
200
|
+
async function generateCmd(spec, route, seed, records, locale) {
|
|
149
201
|
const model = await loadSource(spec);
|
|
150
202
|
if (route) {
|
|
151
203
|
const [method = 'GET', p = '/'] = route.includes(' ') ? route.split(/\s+/) : ['GET', route];
|
|
152
204
|
const [pathname = '/', query] = p.split('?');
|
|
153
|
-
const result = respond(model, { method: method.toUpperCase(), path: pathname, query, seed, records });
|
|
205
|
+
const result = respond(model, { method: method.toUpperCase(), path: pathname, query, seed, records, locale });
|
|
154
206
|
if (result.status >= 400 && !model.routes.some((r) => r.pattern.test(pathname)))
|
|
155
207
|
fail(result.body.message);
|
|
156
208
|
return console.log(JSON.stringify(result.body, null, 2));
|
|
@@ -158,7 +210,7 @@ async function generateCmd(spec, route, seed, records) {
|
|
|
158
210
|
const out = {};
|
|
159
211
|
for (const r of model.routes) {
|
|
160
212
|
const res = r.responses.find((x) => x.status >= 200 && x.status < 300) ?? r.responses[0];
|
|
161
|
-
out[`${r.method} ${r.path}`] = res?.hasExample ? res.example : generate(res?.schema, { seed, key: `${r.method} ${r.path} ${res?.status}` });
|
|
213
|
+
out[`${r.method} ${r.path}`] = res?.hasExample ? res.example : generate(res?.schema, { seed, locale, key: `${r.method} ${r.path} ${res?.status}` });
|
|
162
214
|
}
|
|
163
215
|
console.log(JSON.stringify(out, null, 2));
|
|
164
216
|
}
|
|
@@ -173,7 +225,7 @@ async function infer(dir, out) {
|
|
|
173
225
|
await fs.promises.writeFile(out, text);
|
|
174
226
|
console.error(`${styleText('green', '✔')} Wrote ${model.routes.length} routes to ${out}`);
|
|
175
227
|
}
|
|
176
|
-
async function validate(spec, seed) {
|
|
228
|
+
async function validate(spec, seed, locale) {
|
|
177
229
|
const model = await loadSource(spec);
|
|
178
230
|
printWarnings(model);
|
|
179
231
|
const validator = new ResponseValidator(model.openapi);
|
|
@@ -185,7 +237,7 @@ async function validate(spec, seed) {
|
|
|
185
237
|
if (!res.schema || res.hasExample)
|
|
186
238
|
continue;
|
|
187
239
|
const label = `${r.method} ${r.path} ${res.status}`;
|
|
188
|
-
const value = generate(res.schema, { seed, key: label });
|
|
240
|
+
const value = generate(res.schema, { seed, locale, key: label });
|
|
189
241
|
try {
|
|
190
242
|
const result = validator.validate(res.schema, value);
|
|
191
243
|
if (!result.valid)
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { parseArgs, styleText } from 'node:util';\nimport { generate } from './generate.js';\nimport type { MockModel } from './model.js';\nimport { loadSource } from './load.js';\nimport { formatOpenApi, loadSamples } from './samples.js';\nimport { createMockServer, respond } from './server.js';\nimport { ResponseValidator } from './validate.js';\n\nconst HELP = `mockgen — turn an OpenAPI spec or a folder of sample responses into a running mock server\n\nUsage\n mockgen serve <spec|dir> Start a mock server (hot-reloads on change)\n mockgen generate <spec|dir> [route] Print generated responses, e.g. \"GET /users/1\"\n mockgen validate <spec|dir> Load the input and generate every response\n mockgen infer <dir> [-o file] Write the schema inferred from samples as OpenAPI 3.1\n\nSample folders: name files after the route, e.g. GET.users.json, users/GET.[id].json,\nGET.users.[id].404.json. Folders become path segments; [id] or {id} is a path param.\n\nOptions\n -p, --port <n> Port to listen on (default 4010)\n -H, --host <host> Host to bind (default 127.0.0.1)\n -s, --seed <n> Seed for generated data (default 1)\n -d, --delay <ms> Delay every response, e.g. 300 or 300ms\n -r, --records <n> Records per list endpoint, paged with ?page=&limit= (default 50)\n -o, --out <file> infer: write here instead of stdout (.yaml or .json)\n --no-watch Disable hot reload\n -h, --help Show this help\n -v, --version Show the version\n\nPer request\n X-Mock-Status: 404 (or ?__status=404) Return that status instead of the success response\n X-Mock-Delay: 500 (or ?__delay=500) Delay this response\n ?page=2&limit=20, ?offset=40, ?cursor=… Page through list endpoints\n`;\n\nasync function main(argv: string[]) {\n const { values, positionals } = parseArgs({\n args: argv,\n allowPositionals: true,\n allowNegative: true,\n options: {\n port: { type: 'string', short: 'p', default: '4010' },\n host: { type: 'string', short: 'H', default: '127.0.0.1' },\n seed: { type: 'string', short: 's', default: '1' },\n delay: { type: 'string', short: 'd', default: '0' },\n out: { type: 'string', short: 'o' },\n records: { type: 'string', short: 'r', default: '50' },\n watch: { type: 'boolean', default: true },\n help: { type: 'boolean', short: 'h' },\n version: { type: 'boolean', short: 'v' },\n },\n });\n\n if (values.version) return console.log(await version());\n const [command, spec, ...rest] = positionals;\n if (values.help || !command) return console.log(HELP);\n if (!spec) fail(`Missing <spec>. Try: mockgen ${command} ./openapi.yaml`);\n\n const seed = int(values.seed, '--seed');\n switch (command) {\n case 'serve':\n return serve(spec, { port: int(values.port, '--port'), host: values.host, seed, delay: int(values.delay.replace(/ms$/, ''), '--delay'), watch: values.watch, records: int(values.records, '--records') });\n case 'generate':\n return generateCmd(spec, rest.join(' '), seed, int(values.records, '--records'));\n case 'validate':\n return validate(spec, seed);\n case 'infer':\n return infer(spec, values.out);\n default:\n fail(`Unknown command \"${command}\". Run mockgen --help.`);\n }\n}\n\nasync function serve(spec: string, opts: { port: number; host: string; seed: number; delay: number; watch: boolean; records: number }) {\n const started = performance.now();\n const model = await loadSource(spec);\n printWarnings(model);\n const mock = createMockServer({ model, seed: opts.seed, delay: opts.delay, records: opts.records, log: (line) => console.log(styleText('dim', line)) });\n const url = await mock.listen(opts.port, opts.host).catch((err: NodeJS.ErrnoException) => {\n if (err.code === 'EADDRINUSE') fail(`Port ${opts.port} is already in use on ${opts.host}. Pick another with --port ${opts.port + 1}, or --port 0 for any free port.`);\n if (err.code === 'EACCES') fail(`No permission to use port ${opts.port} on ${opts.host}. Ports below 1024 usually need admin rights; try --port 4010.`);\n throw err;\n });\n\n // Watch before announcing \"ready\": a script that edits the spec as soon as it sees\n // that line would otherwise race the watcher and its edit could be missed.\n if (opts.watch) watchSources(model, async () => {\n try {\n const next = await loadSource(spec);\n mock.setModel(next);\n printWarnings(next);\n console.log(`${styleText('green', '↻')} Reloaded ${next.routes.length} routes`);\n return next;\n } catch (err) {\n console.error(styleText('red', `✖ Reload failed, still serving the previous version:\\n ${(err as Error).message}`));\n return undefined;\n }\n });\n\n console.log(`\\n${styleText('bold', model.title)} ${styleText('dim', model.version)}`);\n for (const r of model.routes) console.log(` ${styleText('cyan', r.method.padEnd(7))} ${url}${model.basePath}${r.path}`);\n console.log(`\\n${styleText('green', '✔')} Mock server ready at ${styleText('bold', url)} in ${Math.round(performance.now() - started)}ms (seed ${opts.seed})\\n`);\n\n const shutdown = () => mock.close().then(() => process.exit(0));\n process.once('SIGINT', shutdown);\n process.once('SIGTERM', shutdown);\n}\n\n/**\n * Watch every local file of the spec and reload on change, debounced.\n *\n * Watchers are re-created after every reload, failed ones included: on Linux and macOS\n * a watcher follows the file, not its name, so an editor's save (write a temp file,\n * rename it over the original) leaves the old watcher on a file that no longer exists.\n */\nfunction watchSources(model: MockModel, reload: () => Promise<MockModel | undefined>) {\n let watchers: fs.FSWatcher[] = [];\n let files = model.sources;\n let timer: NodeJS.Timeout | undefined;\n let retry: NodeJS.Timeout | undefined;\n const onChange = () => {\n clearTimeout(timer);\n timer = setTimeout(async () => {\n files = (await reload())?.sources ?? files;\n watch();\n }, 100);\n };\n const watch = () => {\n watchers.forEach((w) => w.close());\n watchers = [];\n clearTimeout(retry);\n let missing = false;\n for (const file of files) {\n try {\n watchers.push(fs.watch(file, { recursive: fs.statSync(file).isDirectory() }, onChange));\n } catch {\n missing = true; // e.g. mid-save, between delete and rename\n }\n }\n if (missing) retry = setTimeout(watch, 200);\n };\n watch();\n}\n\nasync function generateCmd(spec: string, route: string, seed: number, records: number) {\n const model = await loadSource(spec);\n if (route) {\n const [method = 'GET', p = '/'] = route.includes(' ') ? route.split(/\\s+/) : ['GET', route];\n const [pathname = '/', query] = p.split('?');\n const result = respond(model, { method: method.toUpperCase(), path: pathname, query, seed, records });\n if (result.status >= 400 && !model.routes.some((r) => r.pattern.test(pathname))) fail((result.body as any).message);\n return console.log(JSON.stringify(result.body, null, 2));\n }\n const out: Record<string, unknown> = {};\n for (const r of model.routes) {\n const res = r.responses.find((x) => x.status >= 200 && x.status < 300) ?? r.responses[0];\n out[`${r.method} ${r.path}`] = res?.hasExample ? res.example : generate(res?.schema, { seed, key: `${r.method} ${r.path} ${res?.status}` });\n }\n console.log(JSON.stringify(out, null, 2));\n}\n\nasync function infer(dir: string, out: string | undefined) {\n if (!(await fs.promises.stat(dir).catch(() => undefined))?.isDirectory()) fail(`infer expects a folder of sample responses, got \"${dir}\"`);\n const model = await loadSamples(dir);\n printWarnings(model);\n const text = formatOpenApi(model, out);\n if (!out) return process.stdout.write(text);\n await fs.promises.writeFile(out, text);\n console.error(`${styleText('green', '✔')} Wrote ${model.routes.length} routes to ${out}`);\n}\n\nasync function validate(spec: string, seed: number) {\n const model = await loadSource(spec);\n printWarnings(model);\n const validator = new ResponseValidator(model.openapi);\n let responses = 0;\n let skipped = 0;\n const invalid: string[] = [];\n for (const r of model.routes) {\n for (const res of r.responses) {\n if (!res.schema || res.hasExample) continue;\n const label = `${r.method} ${r.path} ${res.status}`;\n const value = generate(res.schema, { seed, key: label });\n try {\n const result = validator.validate(res.schema, value);\n if (!result.valid) invalid.push(`${label}: ${result.errors}`);\n responses++;\n } catch {\n skipped++; // The schema itself can't be compiled (e.g. a non-JavaScript regex).\n }\n }\n }\n for (const line of invalid) console.error(styleText('red', `✖ ${line}`));\n const summary = `${model.title}: ${model.routes.length} routes, ${responses - invalid.length}/${responses} generated responses valid${skipped ? `, ${skipped} schemas not checkable` : ''}, ${model.warnings.length} warnings`;\n if (invalid.length) fail(summary);\n console.log(`${styleText('green', '✔')} ${summary}`);\n}\n\nfunction printWarnings(model: MockModel) {\n for (const w of model.warnings) console.warn(styleText('yellow', `⚠ ${w}`));\n}\n\nfunction int(value: string, flag: string): number {\n const n = Number(value);\n if (!Number.isInteger(n) || n < 0) fail(`${flag} must be a non-negative integer, got \"${value}\"`);\n return n;\n}\n\nasync function version(): Promise<string> {\n const pkg = JSON.parse(await fs.promises.readFile(path.join(import.meta.dirname, '..', 'package.json'), 'utf8'));\n return pkg.version;\n}\n\nfunction fail(message: string): never {\n console.error(styleText('red', `✖ ${message}`));\n process.exit(1);\n}\n\nmain(process.argv.slice(2)).catch((err) => fail(err.message));\n"]}
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { parseArgs, styleText } from 'node:util';\nimport { assertLocale, generate } from './generate.js';\nimport type { MockModel } from './model.js';\nimport { loadSource } from './load.js';\nimport { formatOpenApi, loadSamples } from './samples.js';\nimport { createMockServer, respond } from './server.js';\nimport { ResponseValidator } from './validate.js';\n\nconst HELP = `mockgen — turn an OpenAPI spec or a folder of sample responses into a running mock server\n\nUsage\n mockgen serve <spec|dir> Start a mock server (hot-reloads on change)\n mockgen generate <spec|dir> [route] Print generated responses, e.g. \"GET /users/1\"\n mockgen validate <spec|dir> Load the input and generate every response\n mockgen infer <dir> [-o file] Write the schema inferred from samples as OpenAPI 3.1\n\nSample folders: name files after the route, e.g. GET.users.json, users/GET.[id].json,\nGET.users.[id].404.json. Folders become path segments; [id] or {id} is a path param.\n\nOptions\n -p, --port <n> Port to listen on (default 4010)\n -H, --host <host> Host to bind (default 127.0.0.1)\n -s, --seed <n> Seed for generated data (default 1)\n -d, --delay <ms> Delay every response, e.g. 300 or 300ms\n -r, --records <n> Records per list endpoint, paged with ?page=&limit= (default 50)\n -l, --locale <code> Language and region for names, addresses and text, e.g. de, ja,\n pt_BR (default en)\n -o, --out <file> infer: write here instead of stdout (.yaml or .json)\n --no-watch Disable hot reload\n -h, --help Show this help\n -v, --version Show the version\n\nPer request\n X-Mock-Status: 404 (or ?__status=404) Return that status instead of the success response\n X-Mock-Delay: 500 (or ?__delay=500) Delay this response\n ?page=2&limit=20, ?offset=40, ?cursor=… Page through list endpoints\n`;\n\nasync function main(argv: string[]) {\n const { values, positionals } = parseArgs({\n args: argv,\n allowPositionals: true,\n allowNegative: true,\n options: {\n port: { type: 'string', short: 'p', default: '4010' },\n host: { type: 'string', short: 'H', default: '127.0.0.1' },\n seed: { type: 'string', short: 's', default: '1' },\n delay: { type: 'string', short: 'd', default: '0' },\n out: { type: 'string', short: 'o' },\n records: { type: 'string', short: 'r', default: '50' },\n locale: { type: 'string', short: 'l' },\n watch: { type: 'boolean', default: true },\n help: { type: 'boolean', short: 'h' },\n version: { type: 'boolean', short: 'v' },\n },\n });\n\n if (values.version) return console.log(await version());\n const [command, spec, ...rest] = positionals;\n if (values.help || !command) return console.log(HELP);\n if (!spec) fail(`Missing <spec>. Try: mockgen ${command} ./openapi.yaml`);\n\n const seed = int(values.seed, '--seed');\n const locale = values.locale;\n if (locale) {\n try {\n assertLocale(locale);\n } catch (err) {\n fail((err as Error).message);\n }\n }\n switch (command) {\n case 'serve':\n return serve(spec, { port: int(values.port, '--port'), host: values.host, seed, delay: int(values.delay.replace(/ms$/, ''), '--delay'), watch: values.watch, records: int(values.records, '--records'), locale });\n case 'generate':\n return generateCmd(spec, rest.join(' '), seed, int(values.records, '--records'), locale);\n case 'validate':\n return validate(spec, seed, locale);\n case 'infer':\n return infer(spec, values.out);\n default:\n fail(`Unknown command \"${command}\". Run mockgen --help.`);\n }\n}\n\nasync function serve(spec: string, opts: { port: number; host: string; seed: number; delay: number; watch: boolean; records: number; locale?: string }) {\n const started = performance.now();\n const model = await loadSource(spec);\n printWarnings(model);\n const mock = createMockServer({ model, seed: opts.seed, delay: opts.delay, records: opts.records, locale: opts.locale, log: (line) => console.log(styleText('dim', line)) });\n const url = await mock.listen(opts.port, opts.host).catch((err: NodeJS.ErrnoException) => {\n if (err.code === 'EADDRINUSE') fail(`Port ${opts.port} is already in use on ${opts.host}. Pick another with --port ${opts.port + 1}, or --port 0 for any free port.`);\n if (err.code === 'EACCES') fail(`No permission to use port ${opts.port} on ${opts.host}. Ports below 1024 usually need admin rights; try --port 4010.`);\n throw err;\n });\n\n // Watch before announcing \"ready\": a script that edits the spec as soon as it sees\n // that line would otherwise race the watcher and its edit could be missed.\n if (opts.watch) watchSources(model, async () => {\n try {\n const next = await loadSource(spec);\n mock.setModel(next);\n printWarnings(next);\n console.log(`${styleText('green', '↻')} Reloaded ${next.routes.length} routes`);\n return next;\n } catch (err) {\n console.error(styleText('red', `✖ Reload failed, still serving the previous version:\\n ${(err as Error).message}`));\n return undefined;\n }\n });\n\n console.log(`\\n${styleText('bold', model.title)} ${styleText('dim', model.version)}`);\n for (const r of model.routes) console.log(` ${styleText('cyan', r.method.padEnd(7))} ${url}${model.basePath}${r.path}`);\n console.log(`\\n${styleText('green', '✔')} Mock server ready at ${styleText('bold', url)} in ${Math.round(performance.now() - started)}ms (seed ${opts.seed})\\n`);\n\n const shutdown = () => mock.close().then(() => process.exit(0));\n process.once('SIGINT', shutdown);\n process.once('SIGTERM', shutdown);\n}\n\n/**\n * Watch the spec's files and reload on change, debounced.\n *\n * Each file is watched through its folder, filtered by name, rather than directly:\n * on Linux and macOS a watcher on a file follows that file, so an editor's save\n * (write a temp file, rename it over the original) would leave it watching a file\n * that no longer exists. A folder watcher sees the rename and keeps working.\n * Sample folders are watched recursively, and any change in them counts.\n */\nfunction watchSources(model: MockModel, reload: () => Promise<MockModel | undefined>) {\n // folder → names of the spec files in it, or 'all' for a sample folder\n const watchers = new Map<string, { watcher: fs.FSWatcher; names: Set<string> | 'all' }>();\n let timer: NodeJS.Timeout | undefined;\n let retry: NodeJS.Timeout | undefined;\n\n const onChange = () => {\n clearTimeout(timer);\n timer = setTimeout(async () => {\n const next = await reload();\n if (next) watch(next.sources);\n }, 100);\n };\n\n const watch = (sources: string[]) => {\n const wanted = new Map<string, Set<string> | 'all'>();\n for (const source of sources) {\n const isDir = fs.statSync(source, { throwIfNoEntry: false })?.isDirectory();\n if (isDir) {\n wanted.set(path.resolve(source), 'all');\n } else {\n const dir = path.dirname(path.resolve(source));\n const names = wanted.get(dir);\n if (names !== 'all') wanted.set(dir, (names ?? new Set()).add(path.basename(source)));\n }\n }\n for (const [dir, entry] of watchers) {\n if (!wanted.has(dir)) {\n entry.watcher.close();\n watchers.delete(dir);\n }\n }\n let failed = false;\n for (const [dir, names] of wanted) {\n const existing = watchers.get(dir);\n if (existing && (existing.names === 'all') === (names === 'all')) {\n existing.names = names;\n continue;\n }\n existing?.watcher.close();\n try {\n const watcher = fs.watch(dir, { recursive: names === 'all' }, (_event, filename) => {\n const current = watchers.get(dir)?.names;\n // Some platforms omit the file name; then any change in the folder counts.\n if (current === 'all' || !filename || current?.has(path.basename(filename.toString()))) onChange();\n });\n // A watcher error (e.g. the folder was deleted) would otherwise crash the server.\n watcher.on('error', () => {\n watcher.close();\n watchers.delete(dir);\n clearTimeout(retry);\n retry = setTimeout(() => watch(sources), 500);\n });\n watchers.set(dir, { watcher, names });\n } catch {\n failed = true; // e.g. the folder doesn't exist right now\n }\n }\n if (failed) {\n clearTimeout(retry);\n retry = setTimeout(() => watch(sources), 500);\n }\n };\n watch(model.sources);\n}\n\nasync function generateCmd(spec: string, route: string, seed: number, records: number, locale?: string) {\n const model = await loadSource(spec);\n if (route) {\n const [method = 'GET', p = '/'] = route.includes(' ') ? route.split(/\\s+/) : ['GET', route];\n const [pathname = '/', query] = p.split('?');\n const result = respond(model, { method: method.toUpperCase(), path: pathname, query, seed, records, locale });\n if (result.status >= 400 && !model.routes.some((r) => r.pattern.test(pathname))) fail((result.body as any).message);\n return console.log(JSON.stringify(result.body, null, 2));\n }\n const out: Record<string, unknown> = {};\n for (const r of model.routes) {\n const res = r.responses.find((x) => x.status >= 200 && x.status < 300) ?? r.responses[0];\n out[`${r.method} ${r.path}`] = res?.hasExample ? res.example : generate(res?.schema, { seed, locale, key: `${r.method} ${r.path} ${res?.status}` });\n }\n console.log(JSON.stringify(out, null, 2));\n}\n\nasync function infer(dir: string, out: string | undefined) {\n if (!(await fs.promises.stat(dir).catch(() => undefined))?.isDirectory()) fail(`infer expects a folder of sample responses, got \"${dir}\"`);\n const model = await loadSamples(dir);\n printWarnings(model);\n const text = formatOpenApi(model, out);\n if (!out) return process.stdout.write(text);\n await fs.promises.writeFile(out, text);\n console.error(`${styleText('green', '✔')} Wrote ${model.routes.length} routes to ${out}`);\n}\n\nasync function validate(spec: string, seed: number, locale?: string) {\n const model = await loadSource(spec);\n printWarnings(model);\n const validator = new ResponseValidator(model.openapi);\n let responses = 0;\n let skipped = 0;\n const invalid: string[] = [];\n for (const r of model.routes) {\n for (const res of r.responses) {\n if (!res.schema || res.hasExample) continue;\n const label = `${r.method} ${r.path} ${res.status}`;\n const value = generate(res.schema, { seed, locale, key: label });\n try {\n const result = validator.validate(res.schema, value);\n if (!result.valid) invalid.push(`${label}: ${result.errors}`);\n responses++;\n } catch {\n skipped++; // The schema itself can't be compiled (e.g. a non-JavaScript regex).\n }\n }\n }\n for (const line of invalid) console.error(styleText('red', `✖ ${line}`));\n const summary = `${model.title}: ${model.routes.length} routes, ${responses - invalid.length}/${responses} generated responses valid${skipped ? `, ${skipped} schemas not checkable` : ''}, ${model.warnings.length} warnings`;\n if (invalid.length) fail(summary);\n console.log(`${styleText('green', '✔')} ${summary}`);\n}\n\nfunction printWarnings(model: MockModel) {\n for (const w of model.warnings) console.warn(styleText('yellow', `⚠ ${w}`));\n}\n\nfunction int(value: string, flag: string): number {\n const n = Number(value);\n if (!Number.isInteger(n) || n < 0) fail(`${flag} must be a non-negative integer, got \"${value}\"`);\n return n;\n}\n\nasync function version(): Promise<string> {\n const pkg = JSON.parse(await fs.promises.readFile(path.join(import.meta.dirname, '..', 'package.json'), 'utf8'));\n return pkg.version;\n}\n\nfunction fail(message: string): never {\n console.error(styleText('red', `✖ ${message}`));\n process.exit(1);\n}\n\nmain(process.argv.slice(2)).catch((err) => fail(err.message));\n"]}
|
package/dist/collections.d.ts
CHANGED
package/dist/collections.js
CHANGED
|
@@ -58,11 +58,11 @@ export function buildPage(input) {
|
|
|
58
58
|
const detailProps = (input.detailSchema?.allOf ? mergeAllOf(input.detailSchema) : input.detailSchema)?.properties ?? {};
|
|
59
59
|
for (let i = win.offset; i < Math.min(total, win.offset + win.limit); i++) {
|
|
60
60
|
const key = `${input.requestPath}#${i}`;
|
|
61
|
-
const id = idKey ? recordId(props[idKey], i, input.seed, key) : undefined;
|
|
61
|
+
const id = idKey ? recordId(props[idKey], i, input.seed, key, input.locale) : undefined;
|
|
62
62
|
const detail = id !== undefined ? input.detail?.(id) : undefined;
|
|
63
63
|
if (detail && !projectable) {
|
|
64
64
|
// Items that are a oneOf/anyOf can't be projected field by field: generate, then overlay.
|
|
65
|
-
const record = generate(shape.itemSchema, { seed: input.seed, key });
|
|
65
|
+
const record = generate(shape.itemSchema, { seed: input.seed, locale: input.locale, key });
|
|
66
66
|
for (const prop of Object.keys(record))
|
|
67
67
|
if (prop in detail && fits(detail[prop], props[prop]))
|
|
68
68
|
record[prop] = detail[prop];
|
|
@@ -70,7 +70,7 @@ export function buildPage(input) {
|
|
|
70
70
|
continue;
|
|
71
71
|
}
|
|
72
72
|
if (!detail) {
|
|
73
|
-
const record = generate(shape.itemSchema, { seed: input.seed, key });
|
|
73
|
+
const record = generate(shape.itemSchema, { seed: input.seed, locale: input.locale, key });
|
|
74
74
|
if (idKey && id !== undefined && fits(id, props[idKey]))
|
|
75
75
|
record[idKey] = id;
|
|
76
76
|
items.push(record);
|
|
@@ -83,12 +83,12 @@ export function buildPage(input) {
|
|
|
83
83
|
if (prop in detail && (detailProps[prop] === propSchema || fits(detail[prop], propSchema)))
|
|
84
84
|
record[prop] = detail[prop];
|
|
85
85
|
else if (required.has(prop))
|
|
86
|
-
record[prop] = generate(propSchema, { seed: input.seed, key: `${key}.${prop}` });
|
|
86
|
+
record[prop] = generate(propSchema, { seed: input.seed, locale: input.locale, key: `${key}.${prop}` });
|
|
87
87
|
}
|
|
88
88
|
// Required keys without a property schema still have to be there.
|
|
89
89
|
for (const prop of required)
|
|
90
90
|
if (!(prop in record))
|
|
91
|
-
record[prop] = prop in detail ? detail[prop] : generate({}, { seed: input.seed, key: `${key}.${prop}` });
|
|
91
|
+
record[prop] = prop in detail ? detail[prop] : generate({}, { seed: input.seed, locale: input.locale, key: `${key}.${prop}` });
|
|
92
92
|
items.push(record);
|
|
93
93
|
}
|
|
94
94
|
const hasMore = win.offset + items.length < total;
|
|
@@ -161,14 +161,14 @@ function fillMeta(obj, schema, m, listKey, depth) {
|
|
|
161
161
|
}
|
|
162
162
|
}
|
|
163
163
|
/** Record i's id: sequential for numeric ids, otherwise generated from the id schema alone. */
|
|
164
|
-
function recordId(idSchema, i, seed, key) {
|
|
164
|
+
function recordId(idSchema, i, seed, key, locale) {
|
|
165
165
|
const t = typeOf(idSchema);
|
|
166
166
|
if (t === 'integer' || t === 'number') {
|
|
167
167
|
const id = Math.max(1, Math.ceil(idSchema.minimum ?? 1)) + i;
|
|
168
168
|
if (idSchema.maximum === undefined || id <= idSchema.maximum)
|
|
169
169
|
return id;
|
|
170
170
|
}
|
|
171
|
-
return generate(idSchema, { seed, key: `${key}.id` });
|
|
171
|
+
return generate(idSchema, { seed, locale, key: `${key}.id` });
|
|
172
172
|
}
|
|
173
173
|
function navLinks(input, win, hasMore) {
|
|
174
174
|
const link = (offset) => {
|
package/dist/collections.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collections.js","sourceRoot":"","sources":["../src/collections.ts"],"names":[],"mappings":"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","sourcesContent":["import { generate } from './generate.js';\nimport { fits, mergeAllOf, typeOf } from './schema.js';\nimport { normalizeName } from './heuristics.js';\nimport type { MockRoute, Schema } from './model.js';\n\n// List endpoints are treated as windows onto a fixed, seeded collection:\n// record i always has the same data, `?page=2` shows records 10–19, and when a\n// detail route exists (`/users` + `/users/{id}`), each list item is overlaid with\n// the detail response for its id so the two always agree.\n\n/** Property names that hold the records in a wrapped list response. */\nconst LIST_KEYS = /^(data|items|results|records|content|rows|entries|list|values|nodes|hits|objects|elements)$/;\n/** Sibling names that mark an object as a page wrapper rather than a record with a nested array. */\nconst PAGE_HINT = /^(total|totalcount|count|page|pages|next|nextpage|cursor|nextcursor|meta|pagination|pageinfo|links|hasmore|hasnext|limit|offset)$/;\n\nexport interface ListShape {\n /** Property holding the records; undefined when the response is a bare array. */\n key?: string;\n arraySchema: Schema;\n itemSchema: Schema;\n}\n\n/** Recognize a list response: a bare array of objects, or an object wrapping one. */\nexport function findListShape(schema: Schema | undefined): ListShape | undefined {\n if (!schema) return undefined;\n if (typeOf(schema) === 'array') {\n return isObjectSchema(schema.items) ? { arraySchema: schema, itemSchema: schema.items } : undefined;\n }\n const props: Record<string, Schema> | undefined = (schema.allOf ? mergeAllOf(schema) : schema).properties;\n if (!props) return undefined;\n const arrays = Object.entries(props).filter(([, s]) => typeOf(s) === 'array' && isObjectSchema(s.items));\n const pick =\n arrays.find(([k]) => LIST_KEYS.test(normalizeName(k))) ??\n (arrays.length === 1 && Object.keys(props).some((k) => PAGE_HINT.test(normalizeName(k))) ? arrays[0] : undefined);\n return pick && { key: pick[0], arraySchema: pick[1], itemSchema: pick[1].items };\n}\n\nfunction isObjectSchema(s: Schema | undefined): s is Schema {\n return !!s && (typeOf(s) === 'object' || !!s.allOf);\n}\n\n/** The `GET /users/{id}` route for list route `GET /users`, if the spec has one. */\nexport function findDetailRoute(routes: MockRoute[], list: MockRoute): MockRoute | undefined {\n const prefix = `${list.path.replace(/\\/$/, '')}/{`;\n return routes.find(\n (r) => r.method === 'GET' && r.path.startsWith(prefix) && r.path.endsWith('}') && !r.path.slice(prefix.length).includes('/'),\n );\n}\n\nexport interface PageWindow {\n offset: number;\n limit: number;\n /** 1-based. */\n page: number;\n total: number;\n}\n\nconst LIMIT_PARAMS = ['limit', 'per_page', 'perPage', 'page_size', 'pageSize', 'size', 'first'];\nconst OFFSET_PARAMS = ['offset', 'skip', 'start'];\nconst PAGE_PARAMS = ['page', 'page_number', 'pageNumber'];\nconst CURSOR_PARAMS = ['cursor', 'after', 'starting_after', 'page_token', 'pageToken'];\n\n/** Work out which slice of the collection a request asks for. */\nexport function readPage(query: URLSearchParams, arraySchema: Schema, total: number, defaultLimit = 10): PageWindow {\n const min = Math.max(1, arraySchema.minItems ?? 1);\n const max = Math.min(1000, arraySchema.maxItems ?? 1000);\n const limit = clamp(firstInt(query, LIMIT_PARAMS) ?? defaultLimit, min, Math.max(min, max));\n const cursor = CURSOR_PARAMS.map((p) => query.get(p)).find(Boolean);\n const page = firstInt(query, PAGE_PARAMS);\n const offset = Math.max(0, (cursor ? decodeCursor(cursor) : undefined) ?? firstInt(query, OFFSET_PARAMS) ?? ((page ?? 1) - 1) * limit);\n return { offset, limit, page: page && page > 0 ? page : Math.floor(offset / limit) + 1, total };\n}\n\nexport interface BuildPageInput {\n /** The list response's schema. */\n schema: Schema;\n /** Concrete request path, used to seed records and build next/prev links. */\n requestPath: string;\n query: URLSearchParams;\n origin?: string;\n seed?: number;\n shape: ListShape;\n /** Generated response body; for wrapped lists, its metadata is filled in place. */\n body: unknown;\n total: number;\n /** Name of the id property that links items to the detail route. */\n idKey?: string;\n /** Detail response for an id, used to make list items agree with `GET /x/{id}`. */\n detail?: (id: unknown) => Record<string, unknown> | undefined;\n /** The detail route's schema; fields it shares with the item need no re-check. */\n detailSchema?: Schema;\n}\n\nexport function buildPage(input: BuildPageInput): { body: unknown; headers: Record<string, string> } {\n const { shape, query, total } = input;\n const win = readPage(query, shape.arraySchema, total);\n const item = shape.itemSchema.allOf ? mergeAllOf(shape.itemSchema) : shape.itemSchema;\n const props: Record<string, Schema> = item.properties ?? {};\n const idKey = input.idKey && input.idKey in props ? input.idKey : 'id' in props ? 'id' : undefined;\n\n const items: unknown[] = [];\n const required = new Set<string>(item.required ?? []);\n const projectable = !item.oneOf && !item.anyOf && !!item.properties;\n const detailProps: Record<string, Schema> = (input.detailSchema?.allOf ? mergeAllOf(input.detailSchema) : input.detailSchema)?.properties ?? {};\n for (let i = win.offset; i < Math.min(total, win.offset + win.limit); i++) {\n const key = `${input.requestPath}#${i}`;\n const id = idKey ? recordId(props[idKey]!, i, input.seed, key) : undefined;\n const detail = id !== undefined ? input.detail?.(id) : undefined;\n if (detail && !projectable) {\n // Items that are a oneOf/anyOf can't be projected field by field: generate, then overlay.\n const record = generate(shape.itemSchema, { seed: input.seed, key }) as Record<string, unknown>;\n for (const prop of Object.keys(record)) if (prop in detail && fits(detail[prop], props[prop])) record[prop] = detail[prop];\n items.push(record);\n continue;\n }\n if (!detail) {\n const record = generate(shape.itemSchema, { seed: input.seed, key }) as Record<string, unknown>;\n if (idKey && id !== undefined && fits(id, props[idKey])) record[idKey] = id;\n items.push(record);\n continue;\n }\n // The detail record is the source of truth: project it onto the item's fields\n // rather than generating the item too (twice the work, on the hottest path).\n const record: Record<string, unknown> = {};\n for (const [prop, propSchema] of Object.entries(props)) {\n if (prop in detail && (detailProps[prop] === propSchema || fits(detail[prop], propSchema))) record[prop] = detail[prop];\n else if (required.has(prop)) record[prop] = generate(propSchema, { seed: input.seed, key: `${key}.${prop}` });\n }\n // Required keys without a property schema still have to be there.\n for (const prop of required) if (!(prop in record)) record[prop] = prop in detail ? detail[prop] : generate({}, { seed: input.seed, key: `${key}.${prop}` });\n items.push(record);\n }\n\n const hasMore = win.offset + items.length < total;\n const headers: Record<string, string> = { 'x-total-count': String(total) };\n const links = navLinks(input, win, hasMore);\n const link = [links.next && `<${links.next}>; rel=\"next\"`, links.prev && `<${links.prev}>; rel=\"prev\"`].filter(Boolean).join(', ');\n if (link) headers.link = link;\n\n if (!shape.key) return { body: items, headers };\n const body = (input.body && typeof input.body === 'object' ? input.body : {}) as Record<string, unknown>;\n body[shape.key] = items;\n const schema = input.schema.allOf ? mergeAllOf(input.schema) : input.schema;\n fillMeta(body, schema, { ...win, count: items.length, hasMore, links }, shape.key, 0);\n return { body, headers };\n}\n\ninterface MetaInfo extends PageWindow {\n count: number;\n hasMore: boolean;\n links: { next?: string; prev?: string; nextCursor?: string; prevCursor?: string };\n}\n\n/** Fill pagination fields (`total`, `page`, `next`, `meta.pagination.totalPages`, …) by name. */\nfunction fillMeta(obj: Record<string, unknown>, schema: Schema | undefined, m: MetaInfo, listKey: string | undefined, depth: number) {\n const props: Record<string, Schema> = schema?.properties ?? {};\n const totalPages = Math.max(1, Math.ceil(m.total / m.limit));\n for (const [key, value] of Object.entries(obj)) {\n if (key === listKey) continue;\n const s = props[key];\n const n = normalizeName(key);\n const set = (v: unknown) => {\n if (fits(v, s)) obj[key] = v;\n };\n // Last page: nullable `next` becomes null, optional `next` disappears.\n const end = () => {\n if (s && (fits(null, s) || s.nullable)) obj[key] = null;\n else if (!(schema?.required ?? []).includes(key)) delete obj[key];\n };\n\n if (/^(total|totalcount|totalitems|totalresults|totalrecords|totalelements|totalentries|count|totalsize)$/.test(n)) set(n === 'count' && 'total' in obj ? m.count : m.total); // `count` next to `total` is the page size\n else if (/^(page|currentpage|pagenumber|pageindex)$/.test(n)) set(m.page);\n else if (/^(perpage|pagesize|limit|size|itemsperpage|pagelength)$/.test(n)) set(m.limit);\n else if (/^(totalpages|pagecount|pages|lastpage)$/.test(n)) set(totalPages);\n else if (/^(offset|skip|start)$/.test(n)) set(m.offset);\n else if (/^(hasmore|hasnext|hasnextpage|more)$/.test(n)) set(m.hasMore);\n else if (/^(hasprev|hasprevious|haspreviouspage)$/.test(n)) set(m.offset > 0);\n else if (/^(next|nextpage|nexturl|nextlink|nextcursor|endcursor|cursor)$/.test(n)) {\n if (!m.hasMore) end();\n else if (typeof value === 'number' || (s && /integer|number/.test(String(typeOf(s))))) set(m.page + 1);\n else set(/cursor/.test(n) ? m.links.nextCursor : m.links.next);\n } else if (/^(prev|previous|prevpage|previouspage|prevurl|prevlink|startcursor)$/.test(n)) {\n if (m.offset === 0) end();\n else if (typeof value === 'number' || (s && /integer|number/.test(String(typeOf(s))))) set(m.page - 1);\n else set(/cursor/.test(n) ? m.links.prevCursor : m.links.prev);\n } else if (depth < 2 && value && typeof value === 'object' && !Array.isArray(value)) {\n fillMeta(value as Record<string, unknown>, s && (s.allOf ? mergeAllOf(s) : s), m, undefined, depth + 1);\n }\n }\n}\n\n/** Record i's id: sequential for numeric ids, otherwise generated from the id schema alone. */\nfunction recordId(idSchema: Schema, i: number, seed: number | undefined, key: string): unknown {\n const t = typeOf(idSchema);\n if (t === 'integer' || t === 'number') {\n const id = Math.max(1, Math.ceil(idSchema.minimum ?? 1)) + i;\n if (idSchema.maximum === undefined || id <= idSchema.maximum) return id;\n }\n return generate(idSchema, { seed, key: `${key}.id` });\n}\n\nfunction navLinks(input: BuildPageInput, win: PageWindow, hasMore: boolean): MetaInfo['links'] {\n const link = (offset: number) => {\n const q = new URLSearchParams(input.query);\n for (const p of CURSOR_PARAMS) q.delete(p);\n const usesOffset = OFFSET_PARAMS.some((p) => q.has(p));\n if (usesOffset) for (const p of OFFSET_PARAMS) q.has(p) && q.set(p, String(offset));\n else q.set(PAGE_PARAMS.find((p) => q.has(p)) ?? 'page', String(Math.floor(offset / win.limit) + 1));\n if (!LIMIT_PARAMS.some((p) => q.has(p))) q.set('limit', String(win.limit));\n return `${input.origin ?? ''}${input.requestPath}?${q}`;\n };\n const prevOffset = Math.max(0, win.offset - win.limit);\n return {\n next: hasMore ? link(win.offset + win.limit) : undefined,\n prev: win.offset > 0 ? link(prevOffset) : undefined,\n nextCursor: hasMore ? encodeCursor(win.offset + win.limit) : undefined,\n prevCursor: win.offset > 0 ? encodeCursor(prevOffset) : undefined,\n };\n}\n\n/** Opaque-looking cursors that are really offsets, so any cursor we hand out works. */\nexport function encodeCursor(offset: number): string {\n return Buffer.from(`o:${offset}`).toString('base64url');\n}\n\nfunction decodeCursor(cursor: string): number | undefined {\n const m = /^o:(\\d+)$/.exec(Buffer.from(cursor, 'base64url').toString());\n return m ? Number(m[1]) : undefined;\n}\n\nfunction firstInt(query: URLSearchParams, names: string[]): number | undefined {\n for (const name of names) {\n const v = query.get(name);\n if (v !== null && /^-?\\d+$/.test(v)) return Number(v);\n }\n return undefined;\n}\n\nfunction clamp(n: number, min: number, max: number): number {\n return Math.min(max, Math.max(min, n));\n}\n"]}
|
|
1
|
+
{"version":3,"file":"collections.js","sourceRoot":"","sources":["../src/collections.ts"],"names":[],"mappings":"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","sourcesContent":["import { generate } from './generate.js';\nimport { fits, mergeAllOf, typeOf } from './schema.js';\nimport { normalizeName } from './heuristics.js';\nimport type { MockRoute, Schema } from './model.js';\n\n// List endpoints are treated as windows onto a fixed, seeded collection:\n// record i always has the same data, `?page=2` shows records 10–19, and when a\n// detail route exists (`/users` + `/users/{id}`), each list item is overlaid with\n// the detail response for its id so the two always agree.\n\n/** Property names that hold the records in a wrapped list response. */\nconst LIST_KEYS = /^(data|items|results|records|content|rows|entries|list|values|nodes|hits|objects|elements)$/;\n/** Sibling names that mark an object as a page wrapper rather than a record with a nested array. */\nconst PAGE_HINT = /^(total|totalcount|count|page|pages|next|nextpage|cursor|nextcursor|meta|pagination|pageinfo|links|hasmore|hasnext|limit|offset)$/;\n\nexport interface ListShape {\n /** Property holding the records; undefined when the response is a bare array. */\n key?: string;\n arraySchema: Schema;\n itemSchema: Schema;\n}\n\n/** Recognize a list response: a bare array of objects, or an object wrapping one. */\nexport function findListShape(schema: Schema | undefined): ListShape | undefined {\n if (!schema) return undefined;\n if (typeOf(schema) === 'array') {\n return isObjectSchema(schema.items) ? { arraySchema: schema, itemSchema: schema.items } : undefined;\n }\n const props: Record<string, Schema> | undefined = (schema.allOf ? mergeAllOf(schema) : schema).properties;\n if (!props) return undefined;\n const arrays = Object.entries(props).filter(([, s]) => typeOf(s) === 'array' && isObjectSchema(s.items));\n const pick =\n arrays.find(([k]) => LIST_KEYS.test(normalizeName(k))) ??\n (arrays.length === 1 && Object.keys(props).some((k) => PAGE_HINT.test(normalizeName(k))) ? arrays[0] : undefined);\n return pick && { key: pick[0], arraySchema: pick[1], itemSchema: pick[1].items };\n}\n\nfunction isObjectSchema(s: Schema | undefined): s is Schema {\n return !!s && (typeOf(s) === 'object' || !!s.allOf);\n}\n\n/** The `GET /users/{id}` route for list route `GET /users`, if the spec has one. */\nexport function findDetailRoute(routes: MockRoute[], list: MockRoute): MockRoute | undefined {\n const prefix = `${list.path.replace(/\\/$/, '')}/{`;\n return routes.find(\n (r) => r.method === 'GET' && r.path.startsWith(prefix) && r.path.endsWith('}') && !r.path.slice(prefix.length).includes('/'),\n );\n}\n\nexport interface PageWindow {\n offset: number;\n limit: number;\n /** 1-based. */\n page: number;\n total: number;\n}\n\nconst LIMIT_PARAMS = ['limit', 'per_page', 'perPage', 'page_size', 'pageSize', 'size', 'first'];\nconst OFFSET_PARAMS = ['offset', 'skip', 'start'];\nconst PAGE_PARAMS = ['page', 'page_number', 'pageNumber'];\nconst CURSOR_PARAMS = ['cursor', 'after', 'starting_after', 'page_token', 'pageToken'];\n\n/** Work out which slice of the collection a request asks for. */\nexport function readPage(query: URLSearchParams, arraySchema: Schema, total: number, defaultLimit = 10): PageWindow {\n const min = Math.max(1, arraySchema.minItems ?? 1);\n const max = Math.min(1000, arraySchema.maxItems ?? 1000);\n const limit = clamp(firstInt(query, LIMIT_PARAMS) ?? defaultLimit, min, Math.max(min, max));\n const cursor = CURSOR_PARAMS.map((p) => query.get(p)).find(Boolean);\n const page = firstInt(query, PAGE_PARAMS);\n const offset = Math.max(0, (cursor ? decodeCursor(cursor) : undefined) ?? firstInt(query, OFFSET_PARAMS) ?? ((page ?? 1) - 1) * limit);\n return { offset, limit, page: page && page > 0 ? page : Math.floor(offset / limit) + 1, total };\n}\n\nexport interface BuildPageInput {\n /** The list response's schema. */\n schema: Schema;\n /** Concrete request path, used to seed records and build next/prev links. */\n requestPath: string;\n query: URLSearchParams;\n origin?: string;\n seed?: number;\n locale?: string;\n shape: ListShape;\n /** Generated response body; for wrapped lists, its metadata is filled in place. */\n body: unknown;\n total: number;\n /** Name of the id property that links items to the detail route. */\n idKey?: string;\n /** Detail response for an id, used to make list items agree with `GET /x/{id}`. */\n detail?: (id: unknown) => Record<string, unknown> | undefined;\n /** The detail route's schema; fields it shares with the item need no re-check. */\n detailSchema?: Schema;\n}\n\nexport function buildPage(input: BuildPageInput): { body: unknown; headers: Record<string, string> } {\n const { shape, query, total } = input;\n const win = readPage(query, shape.arraySchema, total);\n const item = shape.itemSchema.allOf ? mergeAllOf(shape.itemSchema) : shape.itemSchema;\n const props: Record<string, Schema> = item.properties ?? {};\n const idKey = input.idKey && input.idKey in props ? input.idKey : 'id' in props ? 'id' : undefined;\n\n const items: unknown[] = [];\n const required = new Set<string>(item.required ?? []);\n const projectable = !item.oneOf && !item.anyOf && !!item.properties;\n const detailProps: Record<string, Schema> = (input.detailSchema?.allOf ? mergeAllOf(input.detailSchema) : input.detailSchema)?.properties ?? {};\n for (let i = win.offset; i < Math.min(total, win.offset + win.limit); i++) {\n const key = `${input.requestPath}#${i}`;\n const id = idKey ? recordId(props[idKey]!, i, input.seed, key, input.locale) : undefined;\n const detail = id !== undefined ? input.detail?.(id) : undefined;\n if (detail && !projectable) {\n // Items that are a oneOf/anyOf can't be projected field by field: generate, then overlay.\n const record = generate(shape.itemSchema, { seed: input.seed, locale: input.locale, key }) as Record<string, unknown>;\n for (const prop of Object.keys(record)) if (prop in detail && fits(detail[prop], props[prop])) record[prop] = detail[prop];\n items.push(record);\n continue;\n }\n if (!detail) {\n const record = generate(shape.itemSchema, { seed: input.seed, locale: input.locale, key }) as Record<string, unknown>;\n if (idKey && id !== undefined && fits(id, props[idKey])) record[idKey] = id;\n items.push(record);\n continue;\n }\n // The detail record is the source of truth: project it onto the item's fields\n // rather than generating the item too (twice the work, on the hottest path).\n const record: Record<string, unknown> = {};\n for (const [prop, propSchema] of Object.entries(props)) {\n if (prop in detail && (detailProps[prop] === propSchema || fits(detail[prop], propSchema))) record[prop] = detail[prop];\n else if (required.has(prop)) record[prop] = generate(propSchema, { seed: input.seed, locale: input.locale, key: `${key}.${prop}` });\n }\n // Required keys without a property schema still have to be there.\n for (const prop of required) if (!(prop in record)) record[prop] = prop in detail ? detail[prop] : generate({}, { seed: input.seed, locale: input.locale, key: `${key}.${prop}` });\n items.push(record);\n }\n\n const hasMore = win.offset + items.length < total;\n const headers: Record<string, string> = { 'x-total-count': String(total) };\n const links = navLinks(input, win, hasMore);\n const link = [links.next && `<${links.next}>; rel=\"next\"`, links.prev && `<${links.prev}>; rel=\"prev\"`].filter(Boolean).join(', ');\n if (link) headers.link = link;\n\n if (!shape.key) return { body: items, headers };\n const body = (input.body && typeof input.body === 'object' ? input.body : {}) as Record<string, unknown>;\n body[shape.key] = items;\n const schema = input.schema.allOf ? mergeAllOf(input.schema) : input.schema;\n fillMeta(body, schema, { ...win, count: items.length, hasMore, links }, shape.key, 0);\n return { body, headers };\n}\n\ninterface MetaInfo extends PageWindow {\n count: number;\n hasMore: boolean;\n links: { next?: string; prev?: string; nextCursor?: string; prevCursor?: string };\n}\n\n/** Fill pagination fields (`total`, `page`, `next`, `meta.pagination.totalPages`, …) by name. */\nfunction fillMeta(obj: Record<string, unknown>, schema: Schema | undefined, m: MetaInfo, listKey: string | undefined, depth: number) {\n const props: Record<string, Schema> = schema?.properties ?? {};\n const totalPages = Math.max(1, Math.ceil(m.total / m.limit));\n for (const [key, value] of Object.entries(obj)) {\n if (key === listKey) continue;\n const s = props[key];\n const n = normalizeName(key);\n const set = (v: unknown) => {\n if (fits(v, s)) obj[key] = v;\n };\n // Last page: nullable `next` becomes null, optional `next` disappears.\n const end = () => {\n if (s && (fits(null, s) || s.nullable)) obj[key] = null;\n else if (!(schema?.required ?? []).includes(key)) delete obj[key];\n };\n\n if (/^(total|totalcount|totalitems|totalresults|totalrecords|totalelements|totalentries|count|totalsize)$/.test(n)) set(n === 'count' && 'total' in obj ? m.count : m.total); // `count` next to `total` is the page size\n else if (/^(page|currentpage|pagenumber|pageindex)$/.test(n)) set(m.page);\n else if (/^(perpage|pagesize|limit|size|itemsperpage|pagelength)$/.test(n)) set(m.limit);\n else if (/^(totalpages|pagecount|pages|lastpage)$/.test(n)) set(totalPages);\n else if (/^(offset|skip|start)$/.test(n)) set(m.offset);\n else if (/^(hasmore|hasnext|hasnextpage|more)$/.test(n)) set(m.hasMore);\n else if (/^(hasprev|hasprevious|haspreviouspage)$/.test(n)) set(m.offset > 0);\n else if (/^(next|nextpage|nexturl|nextlink|nextcursor|endcursor|cursor)$/.test(n)) {\n if (!m.hasMore) end();\n else if (typeof value === 'number' || (s && /integer|number/.test(String(typeOf(s))))) set(m.page + 1);\n else set(/cursor/.test(n) ? m.links.nextCursor : m.links.next);\n } else if (/^(prev|previous|prevpage|previouspage|prevurl|prevlink|startcursor)$/.test(n)) {\n if (m.offset === 0) end();\n else if (typeof value === 'number' || (s && /integer|number/.test(String(typeOf(s))))) set(m.page - 1);\n else set(/cursor/.test(n) ? m.links.prevCursor : m.links.prev);\n } else if (depth < 2 && value && typeof value === 'object' && !Array.isArray(value)) {\n fillMeta(value as Record<string, unknown>, s && (s.allOf ? mergeAllOf(s) : s), m, undefined, depth + 1);\n }\n }\n}\n\n/** Record i's id: sequential for numeric ids, otherwise generated from the id schema alone. */\nfunction recordId(idSchema: Schema, i: number, seed: number | undefined, key: string, locale?: string): unknown {\n const t = typeOf(idSchema);\n if (t === 'integer' || t === 'number') {\n const id = Math.max(1, Math.ceil(idSchema.minimum ?? 1)) + i;\n if (idSchema.maximum === undefined || id <= idSchema.maximum) return id;\n }\n return generate(idSchema, { seed, locale, key: `${key}.id` });\n}\n\nfunction navLinks(input: BuildPageInput, win: PageWindow, hasMore: boolean): MetaInfo['links'] {\n const link = (offset: number) => {\n const q = new URLSearchParams(input.query);\n for (const p of CURSOR_PARAMS) q.delete(p);\n const usesOffset = OFFSET_PARAMS.some((p) => q.has(p));\n if (usesOffset) for (const p of OFFSET_PARAMS) q.has(p) && q.set(p, String(offset));\n else q.set(PAGE_PARAMS.find((p) => q.has(p)) ?? 'page', String(Math.floor(offset / win.limit) + 1));\n if (!LIMIT_PARAMS.some((p) => q.has(p))) q.set('limit', String(win.limit));\n return `${input.origin ?? ''}${input.requestPath}?${q}`;\n };\n const prevOffset = Math.max(0, win.offset - win.limit);\n return {\n next: hasMore ? link(win.offset + win.limit) : undefined,\n prev: win.offset > 0 ? link(prevOffset) : undefined,\n nextCursor: hasMore ? encodeCursor(win.offset + win.limit) : undefined,\n prevCursor: win.offset > 0 ? encodeCursor(prevOffset) : undefined,\n };\n}\n\n/** Opaque-looking cursors that are really offsets, so any cursor we hand out works. */\nexport function encodeCursor(offset: number): string {\n return Buffer.from(`o:${offset}`).toString('base64url');\n}\n\nfunction decodeCursor(cursor: string): number | undefined {\n const m = /^o:(\\d+)$/.exec(Buffer.from(cursor, 'base64url').toString());\n return m ? Number(m[1]) : undefined;\n}\n\nfunction firstInt(query: URLSearchParams, names: string[]): number | undefined {\n for (const name of names) {\n const v = query.get(name);\n if (v !== null && /^-?\\d+$/.test(v)) return Number(v);\n }\n return undefined;\n}\n\nfunction clamp(n: number, min: number, max: number): number {\n return Math.min(max, Math.max(min, n));\n}\n"]}
|
package/dist/generate.d.ts
CHANGED
|
@@ -6,7 +6,13 @@ export interface GenerateOptions {
|
|
|
6
6
|
key?: string;
|
|
7
7
|
/** Recursion limit for circular schemas. */
|
|
8
8
|
maxDepth?: number;
|
|
9
|
+
/** Faker locale for names, addresses and text, e.g. `de` or `pt_BR`. Default `en`. */
|
|
10
|
+
locale?: string;
|
|
9
11
|
}
|
|
12
|
+
/** Locale codes Faker ships, e.g. `de`, `ja`, `pt_BR`. */
|
|
13
|
+
export declare function availableLocales(): string[];
|
|
14
|
+
/** Throw a readable error for a locale Faker doesn't ship. */
|
|
15
|
+
export declare function assertLocale(locale: string): void;
|
|
10
16
|
/** Generate a realistic, deterministic value that satisfies `schema`. */
|
|
11
17
|
export declare function generate(schema: Schema | undefined, opts?: GenerateOptions): unknown;
|
|
12
18
|
/** 32-bit FNV-1a; stable across platforms and Node versions. */
|
package/dist/generate.js
CHANGED
|
@@ -1,14 +1,50 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import path from 'node:path';
|
|
1
4
|
import { faker as enFaker } from '@faker-js/faker/locale/en';
|
|
2
5
|
import RandExp from 'randexp';
|
|
3
6
|
import { countMatches, discriminatorOf, fits, mergeAllOf, mergeBranch, typeOf } from './schema.js';
|
|
4
7
|
import { looksLikeDate, looksLikeDateTime, numberByName, stringByName } from './heuristics.js';
|
|
5
8
|
// Faker's date helpers default to "now"; pin them so output never drifts.
|
|
6
9
|
const REF_DATE = new Date('2026-01-01T00:00:00.000Z');
|
|
7
|
-
// The package root loads every locale (~23 MB of RSS);
|
|
8
|
-
// exports a shared instance, so build
|
|
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).
|
|
9
13
|
const FakerClass = enFaker.constructor;
|
|
10
|
-
const
|
|
11
|
-
|
|
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');
|
|
12
48
|
// Filling every optional field of every nested object explodes on big specs (Stripe
|
|
13
49
|
// objects nest dozens of expandable objects). Past this depth, or once this many
|
|
14
50
|
// values have been generated, only required fields and minItems are filled.
|
|
@@ -18,6 +54,7 @@ const RICH_DEPTH = 3;
|
|
|
18
54
|
const VALUE_BUDGET = 250;
|
|
19
55
|
/** Generate a realistic, deterministic value that satisfies `schema`. */
|
|
20
56
|
export function generate(schema, opts = {}) {
|
|
57
|
+
faker = fakerFor(opts.locale);
|
|
21
58
|
faker.seed(hash(`${opts.seed ?? 1}:${opts.key ?? ''}`));
|
|
22
59
|
return gen(schema ?? {}, { depth: 0, maxDepth: opts.maxDepth ?? 8, budget: { values: VALUE_BUDGET } });
|
|
23
60
|
}
|
package/dist/generate.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate.js","sourceRoot":"","sources":["../src/generate.ts"],"names":[],"mappings":"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","sourcesContent":["import 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}\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); the `locale/en` entry only\n// exports a shared instance, so build a private one from its class and definitions.\nconst FakerClass = enFaker.constructor as typeof Faker;\nconst faker = new FakerClass({ locale: enFaker.rawDefinitions });\nfaker.setDefaultRefDate(REF_DATE);\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.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"]}
|
|
1
|
+
{"version":3,"file":"generate.js","sourceRoot":"","sources":["../src/generate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,KAAK,IAAI,OAAO,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,OAAO,MAAM,SAAS,CAAC;AAE9B,OAAO,EAAE,YAAY,EAAsB,eAAe,EAAE,IAAI,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACvH,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,YAAY,EAAE,YAAY,EAAsB,MAAM,iBAAiB,CAAC;AAanH,0EAA0E;AAC1E,MAAM,QAAQ,GAAG,IAAI,IAAI,CAAC,0BAA0B,CAAC,CAAC;AAEtD,uFAAuF;AACvF,mFAAmF;AACnF,6DAA6D;AAC7D,MAAM,UAAU,GAAG,OAAO,CAAC,WAA2B,CAAC;AACvD,MAAM,MAAM,GAAG,IAAI,GAAG,CAAgB,CAAC,CAAC,IAAI,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;AACvE,MAAM,OAAO,GAAG,aAAa,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;AAC/C,IAAI,OAA6B,CAAC;AAElC,SAAS,YAAY,CAAC,MAAa;IACjC,MAAM,CAAC,GAAG,IAAI,UAAU,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,cAAc,EAAE,CAAC,CAAC;IAC5D,CAAC,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IAC9B,OAAO,CAAC,CAAC;AACX,CAAC;AAED,0DAA0D;AAC1D,MAAM,UAAU,gBAAgB;IAC9B,OAAO,KAAK,EAAE;SACX,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,2BAA2B,CAAC,CAAC,CAAC;SACvE,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,SAAS,CAAC;SACnD,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;SACrC,IAAI,EAAE,CAAC;IACV,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,8DAA8D;AAC9D,MAAM,UAAU,YAAY,CAAC,MAAc;IACzC,IAAI,CAAC,gBAAgB,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,mBAAmB,MAAM,iBAAiB,gBAAgB,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACvI,CAAC;AAED,mDAAmD;AACnD,SAAS,QAAQ,CAAC,MAAM,GAAG,IAAI;IAC7B,IAAI,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC3B,IAAI,CAAC,CAAC,EAAE,CAAC;QACP,YAAY,CAAC,MAAM,CAAC,CAAC;QACrB,CAAC,GAAG,YAAY,CAAE,OAAO,CAAC,0BAA0B,MAAM,EAAE,CAAsB,CAAC,KAAK,CAAC,CAAC;QAC1F,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IACxB,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED,6GAA6G;AAC7G,IAAI,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAE,CAAC;AAE9B,oFAAoF;AACpF,iFAAiF;AACjF,4EAA4E;AAC5E,kFAAkF;AAClF,iEAAiE;AACjE,MAAM,UAAU,GAAG,CAAC,CAAC;AACrB,MAAM,YAAY,GAAG,GAAG,CAAC;AAazB,yEAAyE;AACzE,MAAM,UAAU,QAAQ,CAAC,MAA0B,EAAE,IAAI,GAAoB,EAAE;IAC7E,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC9B,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,IAAI,CAAC,GAAG,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC;IACxD,OAAO,GAAG,CAAC,MAAM,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,CAAC,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,YAAY,EAAE,EAAE,CAAC,CAAC;AACzG,CAAC;AAED,SAAS,GAAG,CAAC,MAAc,EAAE,GAAQ;IACnC,sFAAsF;IACtF,IAAI,MAAM,CAAC,OAAO,KAAK,SAAS,IAAI,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC;QAAE,OAAO,eAAe,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACzG,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAU,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACnH,IAAI,OAAO,KAAK,SAAS;QAAE,OAAO,eAAe,CAAC,OAAO,CAAC,CAAC;IAC3D,IAAI,OAAO,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC,KAAK,CAAC;IAC3C,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;QACrD,2EAA2E;QAC3E,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,MAAM,CAAC;QACzC,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAU,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,IAAI,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;QACzE,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAU,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC;QACjF,OAAO,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IACvE,CAAC;IAED,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC;QAAE,OAAO,GAAG,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC,CAAC;IACrE,MAAM,OAAO,GAAyB,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,KAAK,CAAC;IACnE,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,MAAM;QAAE,OAAO,SAAS,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC;IAElH,QAAQ,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;QACvB,KAAK,MAAM;YACT,OAAO,IAAI,CAAC;QACd,KAAK,QAAQ;YACX,OAAO,SAAS,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAChC,KAAK,OAAO;YACV,OAAO,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAC/B,KAAK,SAAS;YACZ,OAAO,SAAS,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;QACtC,KAAK,QAAQ;YACX,OAAO,SAAS,CAAC,MAAM,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;QACvC,KAAK,SAAS;YACZ,OAAO,KAAK,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;QAClC,KAAK,QAAQ;YACX,OAAO,SAAS,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAChC;YACE,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;IAC7B,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC;AAE1C,SAAS,IAAI,CAAC,GAAQ;IACpB,OAAO,GAAG,CAAC,OAAO,KAAK,IAAI,IAAI,GAAG,CAAC,KAAK,GAAG,UAAU,IAAI,GAAG,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC;AAClF,CAAC;AAED;;;;GAIG;AACH,SAAS,SAAS,CAAC,MAAc,EAAE,OAAiB,EAAE,SAAkB,EAAE,GAAQ;IAChF,MAAM,IAAI,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;IAChC,MAAM,IAAI,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;IACrC,IAAI,IAAI;QAAE,OAAO,gBAAgB,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;IACnD,mFAAmF;IACnF,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAC/G,IAAI,QAAiB,CAAC;IACtB,yEAAyE;IACzE,mDAAmD;IACnD,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC;IACjC,+EAA+E;IAC/E,qDAAqD;IACrD,KAAK,MAAM,OAAO,IAAI,CAAC,GAAG,EAAE,EAAE,GAAG,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QACvD,KAAK,MAAM,MAAM,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;YACvC,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;YACxE,GAAG,CAAC,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC;YAC3B,MAAM,KAAK,GAAG,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;YAC9B,IAAI,QAAQ,KAAK,SAAS;gBAAE,QAAQ,GAAG,KAAK,CAAC;YAC7C,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;gBAAE,SAAS;YAC9B,IAAI,SAAS,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,YAAY,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC;gBAAE,SAAS;YAC1F,OAAO,KAAK,CAAC;QACf,CAAC;QACD,IAAI,CAAC,SAAS;YAAE,MAAM;IACxB,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,6FAA6F;AAC7F,SAAS,gBAAgB,CAAC,IAAY,EAAE,IAAmB,EAAE,GAAQ;IACnE,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;IAC5C,MAAM,QAAQ,GAAG,CAAC,CAAmC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IAC5G,yFAAyF;IACzF,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;QACvC,MAAM,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;QACtB,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACvE,OAAO,CAAC,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;IACH,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAChF,MAAM,KAAK,GAAG,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC;IACxC,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAG,KAAiC,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC;IACjI,OAAO,KAAK,CAAC;AACf,CAAC;AAED,qFAAqF;AACrF,MAAM,SAAS,GAAG,IAAI,OAAO,EAAkB,CAAC;AAChD,MAAM,YAAY,GAAG,IAAI,OAAO,EAAuC,CAAC;AAExE,qFAAqF;AACrF,SAAS,UAAU,CAAC,MAAc;IAChC,IAAI,IAAI,GAAG,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACjC,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,aAAa,EAAE,GAAG,IAAI,EAAE,GAAG,MAAM,CAAC;QACxD,SAAS,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC;IACvC,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,UAAU,CAAC,KAA6B;IAC/C,IAAI,GAAG,GAAG,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAClC,IAAI,CAAC,GAAG;QAAE,YAAY,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IACnG,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,IAAI,CAAC,CAAS;IACrB,IAAI,CAAC,CAAC,IAAI,IAAI,OAAO,IAAI,CAAC;QAAE,OAAO,CAAC,CAAC;IACrC,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IACpB,IAAI,CAAC,KAAK,QAAQ;QAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,MAAM,IAAI,CAAC,CAAC,CAAC;IACzD,IAAI,CAAC,KAAK,OAAO;QAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,CAAC;IAChD,IAAI,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,KAAK;QAAE,OAAO,CAAC,CAAC;IAC5C,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACnB,CAAC;AAED,SAAS,SAAS,CAAC,MAAc,EAAE,GAAQ;IACzC,MAAM,GAAG,GAA4B,EAAE,CAAC;IACxC,IAAI,GAAG,CAAC,KAAK,IAAI,GAAG,CAAC,QAAQ;QAAE,OAAO,GAAG,CAAC;IAE1C,MAAM,KAAK,GAA2B,MAAM,CAAC,UAAU,IAAI,EAAE,CAAC;IAC9D,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAS,MAAM,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC;IACxD,MAAM,MAAM,GAAkB,EAAE,QAAQ,EAAE,UAAU,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC;IAC/E,KAAK,MAAM,CAAC,IAAI,EAAE,UAAU,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACvD,IAAI,UAAU,CAAC,SAAS;YAAE,SAAS;QACnC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAAE,SAAS;QACzE,GAAG,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;QACpB,GAAG,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,UAAU,EAAE,EAAE,GAAG,GAAG,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,GAAG,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;IAC9E,CAAC;IAED,qEAAqE;IACrE,MAAM,KAAK,GAAG,OAAO,MAAM,CAAC,oBAAoB,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC,CAAC,EAAE,CAAC;IACjG,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;QAC5B,IAAI,CAAC,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,SAAS;YAAE,GAAG,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,KAAK,EAAE,EAAE,GAAG,GAAG,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,GAAG,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;IACxH,CAAC;IAED,IAAI,CAAC,MAAM,CAAC,UAAU,IAAI,MAAM,CAAC,oBAAoB,IAAI,OAAO,MAAM,CAAC,oBAAoB,KAAK,QAAQ,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QACvH,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;QACnD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;YAC/B,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,oBAAoB,EAAE,EAAE,GAAG,GAAG,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,GAAG,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC;QAC/G,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,QAAQ,CAAC,MAAc,EAAE,GAAQ;IACxC,MAAM,GAAG,GAAG,MAAM,CAAC,QAAQ,IAAI,CAAC,CAAC;IACjC,IAAI,GAAG,CAAC,KAAK,IAAI,GAAG,CAAC,QAAQ;QAAE,OAAO,EAAE,CAAC;IACzC,6FAA6F;IAC7F,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,QAAQ,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACnF,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC;IAChG,MAAM,KAAK,GAAW,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC;IACzC,6EAA6E;IAC7E,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,oBAAoB,EAAE,IAAI,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;IAC1G,MAAM,GAAG,GAAc,EAAE,CAAC;IAC1B,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,KAAK,IAAI,QAAQ,GAAG,CAAC,EAAE,GAAG,CAAC,MAAM,GAAG,KAAK,IAAI,QAAQ,GAAG,KAAK,GAAG,CAAC,EAAE,QAAQ,EAAE,EAAE,CAAC;QAC9E,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,EAAE,EAAE,GAAG,GAAG,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,GAAG,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;QACpF,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC;YACvB,MAAM,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;YAChC,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;gBAAE,SAAS;YAC1B,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACd,CAAC;QACD,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAClB,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,SAAS,CAAC,MAAc,EAAE,GAAQ,EAAE,OAAgB;IAC3D,IAAI,EAAE,GAAG,MAAM,CAAC,OAAO,IAAI,CAAC,QAAQ,CAAC;IACrC,IAAI,EAAE,GAAG,MAAM,CAAC,OAAO,IAAI,QAAQ,CAAC;IACpC,4DAA4D;IAC5D,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAChC,IAAI,OAAO,MAAM,CAAC,gBAAgB,KAAK,QAAQ;QAAE,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,MAAM,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;SAC9F,IAAI,MAAM,CAAC,gBAAgB,KAAK,IAAI;QAAE,EAAE,IAAI,IAAI,CAAC;IACtD,IAAI,OAAO,MAAM,CAAC,gBAAgB,KAAK,QAAQ;QAAE,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,MAAM,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;SAC9F,IAAI,MAAM,CAAC,gBAAgB,KAAK,IAAI;QAAE,EAAE,IAAI,IAAI,CAAC;IAEtD,MAAM,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,EAAE,OAAO,IAAI,MAAM,CAAC,MAAM,KAAK,OAAO,IAAI,MAAM,CAAC,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACvI,IAAI,KAAa,CAAC;IAClB,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,IAAI,EAAE,IAAI,MAAM,IAAI,EAAE,EAAE,CAAC;QACzD,KAAK,GAAG,MAAM,CAAC;IACjB,CAAC;SAAM,CAAC;QACN,MAAM,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QAC3E,MAAM,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,IAAI,CAAC;QAClD,KAAK,GAAG,OAAO;YACb,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;YACjE,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,cAAc,EAAE,CAAC,EAAE,CAAC,CAAC;IAC1D,CAAC;IAED,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;QACtB,MAAM,CAAC,GAAG,MAAM,CAAC,UAAU,CAAC;QAC5B,IAAI,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;QACxC,IAAI,OAAO,GAAG,EAAE;YAAE,OAAO,IAAI,CAAC,CAAC;QAC/B,IAAI,OAAO,GAAG,EAAE;YAAE,OAAO,IAAI,CAAC,CAAC;QAC/B,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC;IACtC,CAAC;IACD,IAAI,OAAO;QAAE,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACvC,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,SAAS,CAAC,MAAc,EAAE,GAAQ;IACzC,MAAM,GAAG,GAAG,GAAG,CAAC,MAAM,IAAI,EAAE,QAAQ,EAAE,IAAI,GAAG,EAAU,EAAE,CAAC;IAC1D,8FAA8F;IAC9F,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,IAAI,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAChJ,IAAI,KAAK,GAAG,KAAK,IAAI,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC1E,IAAI,KAAK,KAAK,SAAS,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC;QACpC,IAAI,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;aAChE,IAAI,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;;YACrF,KAAK,GAAG,YAAY,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC;IAC1D,CAAC;IACD,KAAK,KAAK,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;IAEhD,IAAI,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,OAAO;QAAE,OAAO,KAAK,CAAC;IAClD,MAAM,GAAG,GAAG,MAAM,CAAC,SAAS,IAAI,CAAC,CAAC;IAClC,MAAM,GAAG,GAAG,MAAM,CAAC,SAAS,IAAI,QAAQ,CAAC;IACzC,OAAO,KAAK,CAAC,MAAM,GAAG,GAAG;QAAE,KAAK,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,GAAG,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;IACxG,IAAI,KAAK,CAAC,MAAM,GAAG,GAAG;QAAE,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACpD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,8FAA8F;AAC9F,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,CAAC,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;AAE3E,yEAAyE;AACzE,SAAS,WAAW,CAAC,MAA0B,EAAE,SAA6B;IAC5E,IAAI,SAAS,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC9C,IAAI,MAAM,KAAK,OAAO,IAAI,MAAM,KAAK,WAAW;QAAE,OAAO,4BAA4B,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;IAC9H,IAAI,MAAM,KAAK,KAAK,IAAI,MAAM,KAAK,KAAK,IAAI,MAAM,KAAK,KAAK;QAAE,OAAO,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;IACrH,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,QAAQ,CAAC,MAA0B;IAC1C,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,OAAO,CAAC;QACb,KAAK,WAAW;YACd,OAAO,KAAK,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC,WAAW,EAAE,CAAC;QAC9C,KAAK,KAAK,CAAC;QACX,KAAK,KAAK,CAAC;QACX,KAAK,KAAK;YACR,OAAO,KAAK,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC;QAC9B,KAAK,eAAe;YAClB,OAAO,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;QACnC,KAAK,MAAM;YACT,OAAO,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QAC7B,KAAK,MAAM;YACT,OAAO,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;QAChD,KAAK,WAAW;YACd,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;QACrD,KAAK,MAAM;YACT,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,CAAC;QAC7D,KAAK,UAAU;YACb,OAAO,KAAK,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,GAAG,CAAC;QACvD,KAAK,MAAM;YACT,OAAO,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;QAC/B,KAAK,MAAM;YACT,OAAO,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;QAC/B,KAAK,UAAU,CAAC;QAChB,KAAK,cAAc;YACjB,OAAO,KAAK,CAAC,QAAQ,CAAC,UAAU,EAAE,CAAC;QACrC,KAAK,MAAM;YACT,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAC9D,KAAK,QAAQ;YACX,OAAO,KAAK,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;QACvC,KAAK,UAAU;YACb,OAAO,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC;QACjD,KAAK,cAAc;YACjB,OAAO,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;QACxD,KAAK,uBAAuB;YAC1B,OAAO,KAAK,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;QAClC,KAAK,cAAc;YACjB,OAAO,WAAW,KAAK,CAAC,QAAQ,CAAC,UAAU,EAAE,aAAa,CAAC;QAC7D,KAAK,eAAe;YAClB,OAAO,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;QACnC,KAAK,OAAO;YACV,OAAO,cAAc,CAAC;QACxB;YACE,OAAO,SAAS,CAAC;IACrB,CAAC;AACH,CAAC;AAED,SAAS,SAAS,CAAC,OAA2B;IAC5C,IAAI,CAAC,OAAO;QAAE,OAAO,SAAS,CAAC;IAC/B,MAAM,QAAQ,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAC3C,IAAI,CAAC,QAAQ;QAAE,OAAO,SAAS,CAAC;IAChC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;QAC5B,MAAM,SAAS,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC;QACrC,IAAI,QAAQ,CAAC,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC;YAAE,OAAO,SAAS,CAAC;IACpD,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,yFAAyF;AACzF,sFAAsF;AACtF,MAAM,YAAY,GAAG,IAAI,GAAG,EAA+C,CAAC;AAE5E,SAAS,gBAAgB,CAAC,OAAe;IACvC,IAAI,QAAQ,GAAG,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IACzC,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC3B,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;YACjC,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,wCAAwC;YACrD,GAAG,CAAC,OAAO,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC;YAC3D,+EAA+E;YAC/E,GAAG,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACjC,GAAG,CAAC,YAAY,CAAC,QAAQ,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;YACtC,QAAQ,GAAG,EAAE,EAAE,EAAE,IAAI,MAAM,CAAC,OAAO,EAAE,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC;QACnD,CAAC;QAAC,MAAM,CAAC;YACP,QAAQ,GAAG,IAAI,CAAC,CAAC,6DAA6D;QAChF,CAAC;QACD,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IACtC,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,SAAS,OAAO,CAAC,CAAO;IACtB,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AACtC,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,IAAI,CAAC,KAAa;IAChC,IAAI,CAAC,GAAG,UAAU,CAAC;IACnB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QACzB,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;IAC/B,CAAC;IACD,OAAO,CAAC,KAAK,CAAC,CAAC;AACjB,CAAC","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"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type MockServer, type MockServerOptions } from './server.js';
|
|
2
|
-
export { generate, type GenerateOptions } from './generate.js';
|
|
2
|
+
export { availableLocales, generate, type GenerateOptions } from './generate.js';
|
|
3
3
|
export type { MockModel, MockResponse, MockRoute, Schema } from './model.js';
|
|
4
4
|
export { inferSchema, SchemaInferrer } from './infer.js';
|
|
5
5
|
export { loadSource } from './load.js';
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { loadSource } from './load.js';
|
|
2
2
|
import { createMockServer as createServer } from './server.js';
|
|
3
|
-
export { generate } from './generate.js';
|
|
3
|
+
export { availableLocales, generate } from './generate.js';
|
|
4
4
|
export { inferSchema, SchemaInferrer } from './infer.js';
|
|
5
5
|
export { loadSource } from './load.js';
|
|
6
6
|
export { loadOpenApi, SpecLoadError } from './openapi.js';
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,gBAAgB,IAAI,YAAY,EAA2C,MAAM,aAAa,CAAC;AAExG,OAAO,EAAE,QAAQ,EAAwB,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,gBAAgB,IAAI,YAAY,EAA2C,MAAM,aAAa,CAAC;AAExG,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAwB,MAAM,eAAe,CAAC;AAEjF,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACtF,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAyB,MAAM,eAAe,CAAC;AACvF,OAAO,EAAE,OAAO,EAA+E,MAAM,aAAa,CAAC;AAEnH;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,OAAsG;IAEtG,MAAM,KAAK,GAAG,OAAO,IAAI,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAClF,OAAO,YAAY,CAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;AAC7C,CAAC","sourcesContent":["import { loadSource } from './load.js';\nimport { createMockServer as createServer, type MockServer, type MockServerOptions } from './server.js';\n\nexport { availableLocales, generate, type GenerateOptions } from './generate.js';\nexport type { MockModel, MockResponse, MockRoute, Schema } from './model.js';\nexport { inferSchema, SchemaInferrer } from './infer.js';\nexport { loadSource } from './load.js';\nexport { loadOpenApi, SpecLoadError } from './openapi.js';\nexport { formatOpenApi, loadSamples, parseSampleName, toOpenApi } from './samples.js';\nexport { ResponseValidator, toJsonSchema, type ValidationResult } from './validate.js';\nexport { respond, type MockResult, type MockServer, type MockServerOptions, type RespondInput } from './server.js';\n\n/**\n * Create a mock server from a spec path, a sample folder, or an already-loaded model.\n *\n * ```ts\n * const mock = await createMockServer({ spec: './openapi.yaml' });\n * const url = await mock.listen(0);\n * ```\n */\nexport async function createMockServer(\n options: Omit<MockServerOptions, 'model'> & ({ spec: string } | { model: MockServerOptions['model'] }),\n): Promise<MockServer> {\n const model = 'model' in options ? options.model : await loadSource(options.spec);\n return createServer({ ...options, model });\n}\n"]}
|
package/dist/server.d.ts
CHANGED
|
@@ -8,6 +8,8 @@ export interface MockServerOptions {
|
|
|
8
8
|
delay?: number;
|
|
9
9
|
/** Size of every list endpoint's collection, paged through with `?page=`/`?limit=`. Default 50. */
|
|
10
10
|
records?: number;
|
|
11
|
+
/** Faker locale for names, addresses and text, e.g. `de` or `pt_BR`. Default `en`. */
|
|
12
|
+
locale?: string;
|
|
11
13
|
/** Called once per request with a one-line summary. */
|
|
12
14
|
log?: (line: string) => void;
|
|
13
15
|
}
|
|
@@ -36,6 +38,8 @@ export interface RespondInput {
|
|
|
36
38
|
origin?: string;
|
|
37
39
|
/** Collection size for list endpoints. Default 50. */
|
|
38
40
|
records?: number;
|
|
41
|
+
/** Faker locale, e.g. `de`. Default `en`. */
|
|
42
|
+
locale?: string;
|
|
39
43
|
}
|
|
40
44
|
/** Produce the mock response for one request. Pure apart from the generator's seed. */
|
|
41
45
|
export declare function respond(model: MockModel, input: RespondInput): MockResult;
|
package/dist/server.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import http from 'node:http';
|
|
2
2
|
import { buildPage, findDetailRoute, findListShape } from './collections.js';
|
|
3
|
-
import { generate } from './generate.js';
|
|
3
|
+
import { assertLocale, generate } from './generate.js';
|
|
4
4
|
import { fits, mergeAllOf } from './schema.js';
|
|
5
5
|
const CORS_HEADERS = {
|
|
6
6
|
'access-control-allow-origin': '*',
|
|
@@ -9,6 +9,8 @@ const CORS_HEADERS = {
|
|
|
9
9
|
'access-control-expose-headers': '*',
|
|
10
10
|
};
|
|
11
11
|
export function createMockServer(options) {
|
|
12
|
+
if (options.locale)
|
|
13
|
+
assertLocale(options.locale);
|
|
12
14
|
let model = options.model;
|
|
13
15
|
const seed = options.seed ?? 1;
|
|
14
16
|
const server = http.createServer(async (req, res) => {
|
|
@@ -25,6 +27,7 @@ export function createMockServer(options) {
|
|
|
25
27
|
method,
|
|
26
28
|
path: url.pathname,
|
|
27
29
|
seed,
|
|
30
|
+
locale: options.locale,
|
|
28
31
|
status: header(req, 'x-mock-status') ?? url.searchParams.get('__status') ?? undefined,
|
|
29
32
|
query: url.searchParams,
|
|
30
33
|
origin: req.headers.host ? `http://${req.headers.host}` : undefined,
|
|
@@ -87,7 +90,7 @@ export function respond(model, input) {
|
|
|
87
90
|
if (response.hasExample)
|
|
88
91
|
body = structuredClone(response.example);
|
|
89
92
|
else if (response.schema)
|
|
90
|
-
body = generate(response.schema, { seed: input.seed, key: `${route.method} ${input.path} ${status}` });
|
|
93
|
+
body = generate(response.schema, { seed: input.seed, locale: input.locale, key: `${route.method} ${input.path} ${status}` });
|
|
91
94
|
let headers = {};
|
|
92
95
|
// List endpoints (not `/users/{id}`) page through a stable collection.
|
|
93
96
|
const shape = route.method === 'GET' && !response.hasExample && status < 300 && !route.path.endsWith('}') ? findListShape(response.schema) : undefined;
|
|
@@ -102,6 +105,7 @@ export function respond(model, input) {
|
|
|
102
105
|
query: new URLSearchParams(input.query ?? ''),
|
|
103
106
|
origin: input.origin,
|
|
104
107
|
seed: input.seed,
|
|
108
|
+
locale: input.locale,
|
|
105
109
|
shape,
|
|
106
110
|
body,
|
|
107
111
|
total: input.records ?? 50,
|
|
@@ -109,7 +113,7 @@ export function respond(model, input) {
|
|
|
109
113
|
detailSchema: linked ? detailResponse.schema : undefined,
|
|
110
114
|
detail: linked
|
|
111
115
|
? (id) => {
|
|
112
|
-
const r = respond(model, { method: 'GET', path: `${requestPath}/${encodeURIComponent(String(id))}`, seed: input.seed });
|
|
116
|
+
const r = respond(model, { method: 'GET', path: `${requestPath}/${encodeURIComponent(String(id))}`, seed: input.seed, locale: input.locale });
|
|
113
117
|
return r.status < 300 && isRecord(r.body) ? r.body : undefined;
|
|
114
118
|
}
|
|
115
119
|
: undefined,
|
package/dist/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAC7E,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEzC,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AA4B/C,MAAM,YAAY,GAAG;IACnB,6BAA6B,EAAE,GAAG;IAClC,8BAA8B,EAAE,wCAAwC;IACxE,8BAA8B,EAAE,GAAG;IACnC,+BAA+B,EAAE,GAAG;CACrC,CAAC;AAEF,MAAM,UAAU,gBAAgB,CAAC,OAA0B;IACzD,IAAI,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;IAC1B,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,CAAC,CAAC;IAE/B,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;QAClD,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;QAClC,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,EAAE,kBAAkB,CAAC,CAAC;QACxD,MAAM,MAAM,GAAG,CAAC,GAAG,CAAC,MAAM,IAAI,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;QACnD,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,+CAA+C;QAE7D,IAAI,MAAkB,CAAC;QACvB,IAAI,MAAM,KAAK,SAAS,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,CAAC;YAC7E,MAAM,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;QACzD,CAAC;aAAM,CAAC;YACN,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE;gBACtB,MAAM;gBACN,IAAI,EAAE,GAAG,CAAC,QAAQ;gBAClB,IAAI;gBACJ,MAAM,EAAE,MAAM,CAAC,GAAG,EAAE,eAAe,CAAC,IAAI,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,SAAS;gBACrF,KAAK,EAAE,GAAG,CAAC,YAAY;gBACvB,MAAM,EAAE,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS;gBACnE,OAAO,EAAE,OAAO,CAAC,OAAO;aACzB,CAAC,CAAC;QACL,CAAC;QAED,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,cAAc,CAAC,IAAI,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,OAAO,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC;QAC3G,IAAI,KAAK,GAAG,CAAC;YAAE,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC;QAE9D,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,KAAK,SAAS,IAAI,MAAM,KAAK,MAAM,IAAI,MAAM,CAAC,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC;QACjH,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,EAAE;YAC3B,GAAG,YAAY;YACf,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,iCAAiC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACzE,GAAG,MAAM,CAAC,OAAO;SAClB,CAAC,CAAC;QACH,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;QAEpE,OAAO,CAAC,GAAG,EAAE,CAAC,GAAG,MAAM,IAAI,GAAG,CAAC,QAAQ,GAAG,GAAG,CAAC,MAAM,MAAM,MAAM,CAAC,MAAM,IAAI,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1H,CAAC,CAAC,CAAC;IAEH,OAAO;QACL,MAAM;QACN,MAAM,CAAC,IAAI,GAAG,IAAI,EAAE,IAAI,GAAG,WAAW;YACpC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;gBACrC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;gBAC7B,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE;oBAC7B,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,EAAiB,CAAC;oBAC7C,OAAO,CAAC,UAAU,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,GAAG,CAAC,CAAC,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;gBAC5E,CAAC,CAAC,CAAC;YACL,CAAC,CAAC,CAAC;QACL,CAAC;QACD,KAAK;YACH,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;gBACrC,MAAM,CAAC,mBAAmB,EAAE,CAAC;gBAC7B,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;YACzD,CAAC,CAAC,CAAC;QACL,CAAC;QACD,QAAQ,CAAC,IAAI;YACX,KAAK,GAAG,IAAI,CAAC;QACf,CAAC;KACF,CAAC;AACJ,CAAC;AAgBD,uFAAuF;AACvF,MAAM,UAAU,OAAO,CAAC,KAAgB,EAAE,KAAmB;IAC3D,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,SAAS,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;IACjF,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,UAAU;YACf,CAAC,CAAC,WAAW,CAAC,GAAG,EAAE,UAAU,KAAK,CAAC,MAAM,uBAAuB,KAAK,CAAC,IAAI,EAAE,CAAC;YAC7E,CAAC,CAAC,WAAW,CAAC,GAAG,EAAE,gCAAgC,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;IACrF,CAAC;IAED,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC7E,IAAI,MAAM,KAAK,SAAS,IAAI,CAAC,CAAC,MAAM,IAAI,GAAG,IAAI,MAAM,IAAI,GAAG,CAAC,EAAE,CAAC;QAC9D,OAAO,WAAW,CAAC,GAAG,EAAE,mDAAmD,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;IAC9F,CAAC;IAED,MAAM,QAAQ,GAAG,YAAY,CAAC,KAAK,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IACvD,MAAM,MAAM,GAAG,MAAM,IAAI,QAAQ,EAAE,MAAM,IAAI,GAAG,CAAC;IACjD,IAAI,CAAC,QAAQ,IAAI,CAAC,MAAM,KAAK,SAAS,IAAI,QAAQ,CAAC,MAAM,KAAK,MAAM,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;QAC7F,sFAAsF;QACtF,OAAO,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,WAAW,CAAC,MAAM,EAAE,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;IAC9H,CAAC;IAED,IAAI,IAAa,CAAC;IAClB,IAAI,QAAQ,CAAC,UAAU;QAAE,IAAI,GAAG,eAAe,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;SAC7D,IAAI,QAAQ,CAAC,MAAM;QAAE,IAAI,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,IAAI,IAAI,MAAM,EAAE,EAAE,CAAC,CAAC;IAEjI,IAAI,OAAO,GAA2B,EAAE,CAAC;IACzC,uEAAuE;IACvE,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,KAAK,KAAK,IAAI,CAAC,QAAQ,CAAC,UAAU,IAAI,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACvJ,IAAI,KAAK,EAAE,CAAC;QACV,MAAM,WAAW,GAAG,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QACvD,MAAM,WAAW,GAAG,eAAe,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QACzD,MAAM,cAAc,GAAG,WAAW,IAAI,YAAY,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;QAC1E,MAAM,MAAM,GAAG,CAAC,CAAC,cAAc,EAAE,MAAM,IAAI,CAAC,cAAc,CAAC,UAAU,CAAC;QACtE,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,SAAS,CAAC;YAC7B,MAAM,EAAE,QAAQ,CAAC,MAAO;YACxB,WAAW;YACX,KAAK,EAAE,IAAI,eAAe,CAAC,KAAK,CAAC,KAAK,IAAI,EAAE,CAAC;YAC7C,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,KAAK;YACL,IAAI;YACJ,KAAK,EAAE,KAAK,CAAC,OAAO,IAAI,EAAE;YAC1B,KAAK,EAAE,WAAW,EAAE,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;YACrC,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS;YACxD,MAAM,EAAE,MAAM;gBACZ,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE;oBACL,MAAM,CAAC,GAAG,OAAO,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,WAAW,IAAI,kBAAkB,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;oBACxH,OAAO,CAAC,CAAC,MAAM,GAAG,GAAG,IAAI,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;gBACjE,CAAC;gBACH,CAAC,CAAC,SAAS;SACd,CAAC,CAAC,CAAC;IACN,CAAC;IAED,IAAI,QAAQ,CAAC,IAAI,CAAC,IAAI,QAAQ,CAAC,MAAM;QAAE,eAAe,CAAC,IAAI,EAAE,QAAQ,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;IAC7F,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;AACnC,CAAC;AAED,SAAS,SAAS,CAAC,KAAgB,EAAE,MAAc,EAAE,OAAe;IAClE,IAAI,CAAC,GAAG,OAAO,CAAC;IAChB,IAAI,KAAK,CAAC,QAAQ,IAAI,CAAC,CAAC,KAAK,KAAK,CAAC,QAAQ,IAAI,CAAC,CAAC,UAAU,CAAC,GAAG,KAAK,CAAC,QAAQ,GAAG,CAAC,CAAC,EAAE,CAAC;QACnF,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,GAAG,CAAC;IAC5C,CAAC;IACD,IAAI,UAAU,GAAG,KAAK,CAAC;IACvB,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;QACjC,MAAM,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAChC,IAAI,CAAC,CAAC;YAAE,SAAS;QACjB,UAAU,GAAG,IAAI,CAAC;QAClB,IAAI,KAAK,CAAC,MAAM,KAAK,MAAM,IAAI,CAAC,CAAC,MAAM,KAAK,MAAM,IAAI,KAAK,CAAC,MAAM,KAAK,KAAK,CAAC;YAAE,SAAS;QACxF,MAAM,MAAM,GAA2B,EAAE,CAAC;QAC1C,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,kBAAkB,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAE,CAAC,CAAC,CAAC,CAAC;QACtF,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;IACvC,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,EAAE,CAAC;AACtD,CAAC;AAED,SAAS,YAAY,CAAC,SAAyB,EAAE,MAAe;IAC9D,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAC1G,CAAC;IACD,MAAM,QAAQ,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC;IAC3F,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,MAAM,GAAG,GAAG,CAAC,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,QAAQ,CAAC,CAAC,CAAC,CAAC;AACtH,CAAC;AAED,gGAAgG;AAChG,SAAS,eAAe,CAAC,IAA6B,EAAE,MAAc,EAAE,KAAgB,EAAE,MAA8B;IACtH,MAAM,KAAK,GAA2B,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,UAAU,IAAI,EAAE,CAAC;IACpG,MAAM,IAAI,GAAG,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IACrC,KAAK,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACjD,MAAM,GAAG,GAAG,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,IAAI,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;QACnF,IAAI,CAAC,GAAG;YAAE,SAAS;QACnB,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,OAAO,IAAI,CAAC,GAAG,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;QACrF,IAAI,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;YAAE,IAAI,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IACjD,CAAC;AACH,CAAC;AAED,SAAS,QAAQ,CAAC,CAAU;IAC1B,OAAO,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;AAC3D,CAAC;AAED,SAAS,WAAW,CAAC,MAAc,EAAE,OAAe;IAClD,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,OAAO,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;AACjG,CAAC;AAED,SAAS,MAAM,CAAC,GAAyB,EAAE,IAAY;IACrD,MAAM,CAAC,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,OAAO,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACrC,CAAC","sourcesContent":["import http from 'node:http';\nimport type { AddressInfo } from 'node:net';\nimport { buildPage, findDetailRoute, findListShape } from './collections.js';\nimport { generate } from './generate.js';\nimport type { MockModel, MockResponse, MockRoute, Schema } from './model.js';\nimport { fits, mergeAllOf } from './schema.js';\n\nexport interface MockServerOptions {\n model: MockModel;\n /** Seed for generated data; same seed → same responses. Default 1. */\n seed?: number;\n /** Delay every response by this many milliseconds. Overridden per request by `X-Mock-Delay`. */\n delay?: number;\n /** Size of every list endpoint's collection, paged through with `?page=`/`?limit=`. Default 50. */\n records?: number;\n /** Called once per request with a one-line summary. */\n log?: (line: string) => void;\n}\n\nexport interface MockServer {\n server: http.Server;\n listen(port?: number, host?: string): Promise<string>;\n close(): Promise<void>;\n /** Swap the model in place, e.g. after the spec changed on disk. */\n setModel(model: MockModel): void;\n}\n\nexport interface MockResult {\n status: number;\n body: unknown;\n headers: Record<string, string>;\n}\n\nconst CORS_HEADERS = {\n 'access-control-allow-origin': '*',\n 'access-control-allow-methods': 'GET,POST,PUT,PATCH,DELETE,OPTIONS,HEAD',\n 'access-control-allow-headers': '*',\n 'access-control-expose-headers': '*',\n};\n\nexport function createMockServer(options: MockServerOptions): MockServer {\n let model = options.model;\n const seed = options.seed ?? 1;\n\n const server = http.createServer(async (req, res) => {\n const started = performance.now();\n const url = new URL(req.url ?? '/', 'http://localhost');\n const method = (req.method ?? 'GET').toUpperCase();\n req.resume(); // Request bodies are not used yet; drain them.\n\n let result: MockResult;\n if (method === 'OPTIONS' && !findRoute(model, 'OPTIONS', url.pathname).route) {\n result = { status: 204, body: undefined, headers: {} };\n } else {\n result = respond(model, {\n method,\n path: url.pathname,\n seed,\n status: header(req, 'x-mock-status') ?? url.searchParams.get('__status') ?? undefined,\n query: url.searchParams,\n origin: req.headers.host ? `http://${req.headers.host}` : undefined,\n records: options.records,\n });\n }\n\n const delay = Number(header(req, 'x-mock-delay') ?? url.searchParams.get('__delay') ?? options.delay ?? 0);\n if (delay > 0) await new Promise((r) => setTimeout(r, delay));\n\n const hasBody = result.body !== undefined && method !== 'HEAD' && result.status !== 204 && result.status !== 304;\n res.writeHead(result.status, {\n ...CORS_HEADERS,\n ...(hasBody ? { 'content-type': 'application/json; charset=utf-8' } : {}),\n ...result.headers,\n });\n res.end(hasBody ? JSON.stringify(result.body, null, 2) : undefined);\n\n options.log?.(`${method} ${url.pathname}${url.search} → ${result.status} ${Math.round(performance.now() - started)}ms`);\n });\n\n return {\n server,\n listen(port = 4010, host = '127.0.0.1') {\n return new Promise((resolve, reject) => {\n server.once('error', reject);\n server.listen(port, host, () => {\n const addr = server.address() as AddressInfo;\n resolve(`http://${host.includes(':') ? `[${host}]` : host}:${addr.port}`);\n });\n });\n },\n close() {\n return new Promise((resolve, reject) => {\n server.closeAllConnections();\n server.close((err) => (err ? reject(err) : resolve()));\n });\n },\n setModel(next) {\n model = next;\n },\n };\n}\n\nexport interface RespondInput {\n method: string;\n path: string;\n seed?: number;\n /** Force a status code, e.g. from `X-Mock-Status`. */\n status?: string | number;\n /** Query string; drives pagination on list endpoints. */\n query?: URLSearchParams | string;\n /** `http://host:port`, used to build absolute next/prev links. */\n origin?: string;\n /** Collection size for list endpoints. Default 50. */\n records?: number;\n}\n\n/** Produce the mock response for one request. Pure apart from the generator's seed. */\nexport function respond(model: MockModel, input: RespondInput): MockResult {\n const { route, params, pathExists } = findRoute(model, input.method, input.path);\n if (!route) {\n return pathExists\n ? errorResult(405, `Method ${input.method} is not defined for ${input.path}`)\n : errorResult(404, `No route in the spec matches ${input.method} ${input.path}`);\n }\n\n const forced = input.status !== undefined ? Number(input.status) : undefined;\n if (forced !== undefined && !(forced >= 100 && forced <= 599)) {\n return errorResult(400, `X-Mock-Status must be an HTTP status code, got \"${input.status}\"`);\n }\n\n const response = pickResponse(route.responses, forced);\n const status = forced ?? response?.status ?? 200;\n if (!response || (forced !== undefined && response.status !== forced && !response.isDefault)) {\n // The spec doesn't describe this status; still honor it so error paths can be tested.\n return status >= 400 ? errorResult(status, http.STATUS_CODES[status] ?? 'Error') : { status, body: undefined, headers: {} };\n }\n\n let body: unknown;\n if (response.hasExample) body = structuredClone(response.example);\n else if (response.schema) body = generate(response.schema, { seed: input.seed, key: `${route.method} ${input.path} ${status}` });\n\n let headers: Record<string, string> = {};\n // List endpoints (not `/users/{id}`) page through a stable collection.\n const shape = route.method === 'GET' && !response.hasExample && status < 300 && !route.path.endsWith('}') ? findListShape(response.schema) : undefined;\n if (shape) {\n const requestPath = input.path.replace(/(.)\\/$/, '$1');\n const detailRoute = findDetailRoute(model.routes, route);\n const detailResponse = detailRoute && pickResponse(detailRoute.responses);\n const linked = !!detailResponse?.schema && !detailResponse.hasExample;\n ({ body, headers } = buildPage({\n schema: response.schema!,\n requestPath,\n query: new URLSearchParams(input.query ?? ''),\n origin: input.origin,\n seed: input.seed,\n shape,\n body,\n total: input.records ?? 50,\n idKey: detailRoute?.paramNames.at(-1),\n detailSchema: linked ? detailResponse.schema : undefined,\n detail: linked\n ? (id) => {\n const r = respond(model, { method: 'GET', path: `${requestPath}/${encodeURIComponent(String(id))}`, seed: input.seed });\n return r.status < 300 && isRecord(r.body) ? r.body : undefined;\n }\n : undefined,\n }));\n }\n\n if (isRecord(body) && response.schema) applyPathParams(body, response.schema, route, params);\n return { status, body, headers };\n}\n\nfunction findRoute(model: MockModel, method: string, rawPath: string) {\n let p = rawPath;\n if (model.basePath && (p === model.basePath || p.startsWith(`${model.basePath}/`))) {\n p = p.slice(model.basePath.length) || '/';\n }\n let pathExists = false;\n for (const route of model.routes) {\n const m = route.pattern.exec(p);\n if (!m) continue;\n pathExists = true;\n if (route.method !== method && !(method === 'HEAD' && route.method === 'GET')) continue;\n const params: Record<string, string> = {};\n route.paramNames.forEach((name, i) => (params[name] = decodeURIComponent(m[i + 1]!)));\n return { route, params, pathExists };\n }\n return { route: undefined, params: {}, pathExists };\n}\n\nfunction pickResponse(responses: MockResponse[], forced?: number): MockResponse | undefined {\n if (forced !== undefined) {\n return responses.find((r) => r.status === forced && !r.isDefault) ?? responses.find((r) => r.isDefault);\n }\n const explicit = responses.filter((r) => !r.isDefault).sort((a, b) => a.status - b.status);\n return explicit.find((r) => r.status >= 200 && r.status < 300) ?? responses.find((r) => r.isDefault) ?? explicit[0];\n}\n\n/** `GET /users/42` returns a user whose `id` is 42 — when 42 is a valid `id` for the schema. */\nfunction applyPathParams(body: Record<string, unknown>, schema: Schema, route: MockRoute, params: Record<string, string>) {\n const props: Record<string, Schema> = (schema.allOf ? mergeAllOf(schema) : schema).properties ?? {};\n const last = route.paramNames.at(-1);\n for (const [name, raw] of Object.entries(params)) {\n const key = name in body ? name : name === last && 'id' in body ? 'id' : undefined;\n if (!key) continue;\n const value = raw.trim() !== '' && typeof body[key] === 'number' ? Number(raw) : raw;\n if (fits(value, props[key])) body[key] = value;\n }\n}\n\nfunction isRecord(v: unknown): v is Record<string, unknown> {\n return !!v && typeof v === 'object' && !Array.isArray(v);\n}\n\nfunction errorResult(status: number, message: string): MockResult {\n return { status, body: { error: http.STATUS_CODES[status] ?? 'Error', message }, headers: {} };\n}\n\nfunction header(req: http.IncomingMessage, name: string): string | undefined {\n const v = req.headers[name];\n return Array.isArray(v) ? v[0] : v;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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","sourcesContent":["import http from 'node:http';\nimport type { AddressInfo } from 'node:net';\nimport { buildPage, findDetailRoute, findListShape } from './collections.js';\nimport { assertLocale, generate } from './generate.js';\nimport type { MockModel, MockResponse, MockRoute, Schema } from './model.js';\nimport { fits, mergeAllOf } from './schema.js';\n\nexport interface MockServerOptions {\n model: MockModel;\n /** Seed for generated data; same seed → same responses. Default 1. */\n seed?: number;\n /** Delay every response by this many milliseconds. Overridden per request by `X-Mock-Delay`. */\n delay?: number;\n /** Size of every list endpoint's collection, paged through with `?page=`/`?limit=`. Default 50. */\n records?: number;\n /** Faker locale for names, addresses and text, e.g. `de` or `pt_BR`. Default `en`. */\n locale?: string;\n /** Called once per request with a one-line summary. */\n log?: (line: string) => void;\n}\n\nexport interface MockServer {\n server: http.Server;\n listen(port?: number, host?: string): Promise<string>;\n close(): Promise<void>;\n /** Swap the model in place, e.g. after the spec changed on disk. */\n setModel(model: MockModel): void;\n}\n\nexport interface MockResult {\n status: number;\n body: unknown;\n headers: Record<string, string>;\n}\n\nconst CORS_HEADERS = {\n 'access-control-allow-origin': '*',\n 'access-control-allow-methods': 'GET,POST,PUT,PATCH,DELETE,OPTIONS,HEAD',\n 'access-control-allow-headers': '*',\n 'access-control-expose-headers': '*',\n};\n\nexport function createMockServer(options: MockServerOptions): MockServer {\n if (options.locale) assertLocale(options.locale);\n let model = options.model;\n const seed = options.seed ?? 1;\n\n const server = http.createServer(async (req, res) => {\n const started = performance.now();\n const url = new URL(req.url ?? '/', 'http://localhost');\n const method = (req.method ?? 'GET').toUpperCase();\n req.resume(); // Request bodies are not used yet; drain them.\n\n let result: MockResult;\n if (method === 'OPTIONS' && !findRoute(model, 'OPTIONS', url.pathname).route) {\n result = { status: 204, body: undefined, headers: {} };\n } else {\n result = respond(model, {\n method,\n path: url.pathname,\n seed,\n locale: options.locale,\n status: header(req, 'x-mock-status') ?? url.searchParams.get('__status') ?? undefined,\n query: url.searchParams,\n origin: req.headers.host ? `http://${req.headers.host}` : undefined,\n records: options.records,\n });\n }\n\n const delay = Number(header(req, 'x-mock-delay') ?? url.searchParams.get('__delay') ?? options.delay ?? 0);\n if (delay > 0) await new Promise((r) => setTimeout(r, delay));\n\n const hasBody = result.body !== undefined && method !== 'HEAD' && result.status !== 204 && result.status !== 304;\n res.writeHead(result.status, {\n ...CORS_HEADERS,\n ...(hasBody ? { 'content-type': 'application/json; charset=utf-8' } : {}),\n ...result.headers,\n });\n res.end(hasBody ? JSON.stringify(result.body, null, 2) : undefined);\n\n options.log?.(`${method} ${url.pathname}${url.search} → ${result.status} ${Math.round(performance.now() - started)}ms`);\n });\n\n return {\n server,\n listen(port = 4010, host = '127.0.0.1') {\n return new Promise((resolve, reject) => {\n server.once('error', reject);\n server.listen(port, host, () => {\n const addr = server.address() as AddressInfo;\n resolve(`http://${host.includes(':') ? `[${host}]` : host}:${addr.port}`);\n });\n });\n },\n close() {\n return new Promise((resolve, reject) => {\n server.closeAllConnections();\n server.close((err) => (err ? reject(err) : resolve()));\n });\n },\n setModel(next) {\n model = next;\n },\n };\n}\n\nexport interface RespondInput {\n method: string;\n path: string;\n seed?: number;\n /** Force a status code, e.g. from `X-Mock-Status`. */\n status?: string | number;\n /** Query string; drives pagination on list endpoints. */\n query?: URLSearchParams | string;\n /** `http://host:port`, used to build absolute next/prev links. */\n origin?: string;\n /** Collection size for list endpoints. Default 50. */\n records?: number;\n /** Faker locale, e.g. `de`. Default `en`. */\n locale?: string;\n}\n\n/** Produce the mock response for one request. Pure apart from the generator's seed. */\nexport function respond(model: MockModel, input: RespondInput): MockResult {\n const { route, params, pathExists } = findRoute(model, input.method, input.path);\n if (!route) {\n return pathExists\n ? errorResult(405, `Method ${input.method} is not defined for ${input.path}`)\n : errorResult(404, `No route in the spec matches ${input.method} ${input.path}`);\n }\n\n const forced = input.status !== undefined ? Number(input.status) : undefined;\n if (forced !== undefined && !(forced >= 100 && forced <= 599)) {\n return errorResult(400, `X-Mock-Status must be an HTTP status code, got \"${input.status}\"`);\n }\n\n const response = pickResponse(route.responses, forced);\n const status = forced ?? response?.status ?? 200;\n if (!response || (forced !== undefined && response.status !== forced && !response.isDefault)) {\n // The spec doesn't describe this status; still honor it so error paths can be tested.\n return status >= 400 ? errorResult(status, http.STATUS_CODES[status] ?? 'Error') : { status, body: undefined, headers: {} };\n }\n\n let body: unknown;\n if (response.hasExample) body = structuredClone(response.example);\n else if (response.schema) body = generate(response.schema, { seed: input.seed, locale: input.locale, key: `${route.method} ${input.path} ${status}` });\n\n let headers: Record<string, string> = {};\n // List endpoints (not `/users/{id}`) page through a stable collection.\n const shape = route.method === 'GET' && !response.hasExample && status < 300 && !route.path.endsWith('}') ? findListShape(response.schema) : undefined;\n if (shape) {\n const requestPath = input.path.replace(/(.)\\/$/, '$1');\n const detailRoute = findDetailRoute(model.routes, route);\n const detailResponse = detailRoute && pickResponse(detailRoute.responses);\n const linked = !!detailResponse?.schema && !detailResponse.hasExample;\n ({ body, headers } = buildPage({\n schema: response.schema!,\n requestPath,\n query: new URLSearchParams(input.query ?? ''),\n origin: input.origin,\n seed: input.seed,\n locale: input.locale,\n shape,\n body,\n total: input.records ?? 50,\n idKey: detailRoute?.paramNames.at(-1),\n detailSchema: linked ? detailResponse.schema : undefined,\n detail: linked\n ? (id) => {\n const r = respond(model, { method: 'GET', path: `${requestPath}/${encodeURIComponent(String(id))}`, seed: input.seed, locale: input.locale });\n return r.status < 300 && isRecord(r.body) ? r.body : undefined;\n }\n : undefined,\n }));\n }\n\n if (isRecord(body) && response.schema) applyPathParams(body, response.schema, route, params);\n return { status, body, headers };\n}\n\nfunction findRoute(model: MockModel, method: string, rawPath: string) {\n let p = rawPath;\n if (model.basePath && (p === model.basePath || p.startsWith(`${model.basePath}/`))) {\n p = p.slice(model.basePath.length) || '/';\n }\n let pathExists = false;\n for (const route of model.routes) {\n const m = route.pattern.exec(p);\n if (!m) continue;\n pathExists = true;\n if (route.method !== method && !(method === 'HEAD' && route.method === 'GET')) continue;\n const params: Record<string, string> = {};\n route.paramNames.forEach((name, i) => (params[name] = decodeURIComponent(m[i + 1]!)));\n return { route, params, pathExists };\n }\n return { route: undefined, params: {}, pathExists };\n}\n\nfunction pickResponse(responses: MockResponse[], forced?: number): MockResponse | undefined {\n if (forced !== undefined) {\n return responses.find((r) => r.status === forced && !r.isDefault) ?? responses.find((r) => r.isDefault);\n }\n const explicit = responses.filter((r) => !r.isDefault).sort((a, b) => a.status - b.status);\n return explicit.find((r) => r.status >= 200 && r.status < 300) ?? responses.find((r) => r.isDefault) ?? explicit[0];\n}\n\n/** `GET /users/42` returns a user whose `id` is 42 — when 42 is a valid `id` for the schema. */\nfunction applyPathParams(body: Record<string, unknown>, schema: Schema, route: MockRoute, params: Record<string, string>) {\n const props: Record<string, Schema> = (schema.allOf ? mergeAllOf(schema) : schema).properties ?? {};\n const last = route.paramNames.at(-1);\n for (const [name, raw] of Object.entries(params)) {\n const key = name in body ? name : name === last && 'id' in body ? 'id' : undefined;\n if (!key) continue;\n const value = raw.trim() !== '' && typeof body[key] === 'number' ? Number(raw) : raw;\n if (fits(value, props[key])) body[key] = value;\n }\n}\n\nfunction isRecord(v: unknown): v is Record<string, unknown> {\n return !!v && typeof v === 'object' && !Array.isArray(v);\n}\n\nfunction errorResult(status: number, message: string): MockResult {\n return { status, body: { error: http.STATUS_CODES[status] ?? 'Error', message }, headers: {} };\n}\n\nfunction header(req: http.IncomingMessage, name: string): string | undefined {\n const v = req.headers[name];\n return Array.isArray(v) ? v[0] : v;\n}\n"]}
|
package/package.json
CHANGED