api-json-server 1.0.1 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/README.md +636 -0
  2. package/dist/behavior.js +44 -0
  3. package/dist/history/historyRecorder.js +66 -0
  4. package/dist/history/types.js +2 -0
  5. package/dist/index.js +126 -5
  6. package/dist/loadSpec.js +32 -0
  7. package/dist/logger/customLogger.js +75 -0
  8. package/dist/logger/formatters.js +82 -0
  9. package/dist/logger/types.js +2 -0
  10. package/dist/openapi.js +152 -0
  11. package/dist/registerEndpoints.js +97 -0
  12. package/dist/requestMatch.js +99 -0
  13. package/dist/responseRenderer.js +98 -0
  14. package/dist/server.js +210 -0
  15. package/dist/spec.js +146 -0
  16. package/dist/stringTemplate.js +55 -0
  17. package/examples/auth-variants.json +31 -0
  18. package/examples/basic-crud.json +46 -0
  19. package/examples/companies-nested.json +47 -0
  20. package/examples/orders-and-matches.json +49 -0
  21. package/examples/users-faker.json +35 -0
  22. package/mock.spec.json +1 -0
  23. package/mockserve.spec.schema.json +7 -0
  24. package/package.json +20 -3
  25. package/scripts/build-schema.ts +21 -0
  26. package/src/behavior.ts +56 -0
  27. package/src/history/historyRecorder.ts +77 -0
  28. package/src/history/types.ts +25 -0
  29. package/src/index.ts +124 -85
  30. package/src/loadSpec.ts +5 -2
  31. package/src/logger/customLogger.ts +85 -0
  32. package/src/logger/formatters.ts +74 -0
  33. package/src/logger/types.ts +30 -0
  34. package/src/openapi.ts +203 -0
  35. package/src/registerEndpoints.ts +94 -162
  36. package/src/requestMatch.ts +104 -0
  37. package/src/responseRenderer.ts +112 -0
  38. package/src/server.ts +236 -14
  39. package/src/spec.ts +108 -8
  40. package/src/stringTemplate.ts +55 -0
  41. package/tests/behavior.test.ts +88 -0
  42. package/tests/cors.test.ts +128 -0
  43. package/tests/faker.test.ts +175 -0
  44. package/tests/fixtures/spec.basic.json +39 -0
  45. package/tests/headers.test.ts +124 -0
  46. package/tests/helpers.ts +28 -0
  47. package/tests/history.test.ts +188 -0
  48. package/tests/matching.test.ts +245 -0
  49. package/tests/server.test.ts +73 -0
  50. package/tests/template.test.ts +90 -0
  51. package/src/template.ts +0 -61
package/README.md ADDED
@@ -0,0 +1,636 @@
1
+ # api-json-server
2
+
3
+ A powerful, feature-rich mock API server driven by a JSON spec. Designed for fast development, repeatable mock data, comprehensive testing, and clear API documentation.
4
+
5
+ ## Highlights
6
+
7
+ - **JSON-driven spec** - Define endpoints, responses, and matching rules in a simple JSON file
8
+ - **Advanced matching** - Match requests by query params, body, headers, and cookies
9
+ - **Custom headers** - Set response headers with template support
10
+ - **CORS configuration** - Global and per-endpoint CORS settings
11
+ - **Faker integration** - Generate realistic fake data (names, emails, phone numbers, companies, dates, etc.)
12
+ - **Response templating** - Use request data (params, query, body) in responses
13
+ - **Variants** - Define alternate responses based on match rules
14
+ - **Request history** - Record and inspect all incoming requests for debugging
15
+ - **Variable delays** - Simulate realistic network latency with random delay ranges
16
+ - **Error simulation** - Control error rates and responses
17
+ - **OpenAPI/Swagger** - Auto-generated OpenAPI docs with built-in Swagger UI
18
+ - **Hot reload** - Auto-reload when spec file changes (with `--watch`)
19
+ - **Beautiful logging** - Color-coded, readable console output
20
+
21
+ ## Installation
22
+
23
+ ```bash
24
+ npm install
25
+ ```
26
+
27
+ ## Quick Start
28
+
29
+ 1) Create a spec file (`mock.spec.json`):
30
+
31
+ ```json
32
+ {
33
+ "version": 1,
34
+ "settings": {
35
+ "delayMs": 0,
36
+ "errorRate": 0
37
+ },
38
+ "endpoints": [
39
+ {
40
+ "method": "GET",
41
+ "path": "/users/:id",
42
+ "response": {
43
+ "id": "{{params.id}}",
44
+ "name": { "__faker": "person.fullName" },
45
+ "email": { "__faker": "internet.email" },
46
+ "avatar": { "__faker": "image.avatar" }
47
+ }
48
+ }
49
+ ]
50
+ }
51
+ ```
52
+
53
+ 2) Start the server:
54
+
55
+ ```bash
56
+ npm run dev -- serve --spec mock.spec.json
57
+ ```
58
+
59
+ 3) Test the endpoint:
60
+
61
+ ```bash
62
+ curl "http://localhost:3000/users/42"
63
+ ```
64
+
65
+ Response:
66
+ ```json
67
+ {
68
+ "id": "42",
69
+ "name": "Jane Doe",
70
+ "email": "jane.doe@example.com",
71
+ "avatar": "https://cloudflare-ipfs.com/ipfs/..."
72
+ }
73
+ ```
74
+
75
+ ## CLI
76
+
77
+ ```bash
78
+ mockserve serve [options]
79
+ ```
80
+
81
+ ### Options
82
+
83
+ - `--spec <path>` - Path to the JSON spec file (default: `mock.spec.json`)
84
+ - `--port <number>` - Port to run the server on (default: `3000`)
85
+ - `--watch` / `--no-watch` - Auto-reload when spec changes (default: enabled)
86
+ - `--base-url <url>` - Public base URL for OpenAPI servers[] (e.g., `https://api.example.com`)
87
+ - `--log-format <format>` - Log format: `pretty` or `json` (default: `pretty`)
88
+ - `--log-level <level>` - Log level: `trace`, `debug`, `info`, `warn`, `error`, `fatal` (default: `info`)
89
+
90
+ ### Examples
91
+
92
+ ```bash
93
+ # Start with custom port
94
+ mockserve serve --port 8080
95
+
96
+ # Use JSON logging format
97
+ mockserve serve --log-format json
98
+
99
+ # Disable auto-reload
100
+ mockserve serve --no-watch
101
+
102
+ # Set base URL for OpenAPI
103
+ mockserve serve --base-url https://api.mysite.com
104
+ ```
105
+
106
+ ## Spec Reference
107
+
108
+ The spec is validated by `mockserve.spec.schema.json`. It consists of:
109
+
110
+ ```json
111
+ {
112
+ "version": 1,
113
+ "settings": { ... },
114
+ "endpoints": [ ... ]
115
+ }
116
+ ```
117
+
118
+ ### Settings
119
+
120
+ Global settings that apply to all endpoints unless overridden:
121
+
122
+ ```json
123
+ {
124
+ "delayMs": 0,
125
+ "errorRate": 0,
126
+ "errorStatus": 500,
127
+ "errorResponse": { "error": "Mock error" },
128
+ "fakerSeed": 12345,
129
+ "cors": {
130
+ "origin": "*",
131
+ "credentials": true
132
+ }
133
+ }
134
+ ```
135
+
136
+ #### Settings Fields
137
+
138
+ - **`delayMs`** (number): Fixed delay in milliseconds before responding
139
+ - **`errorRate`** (number): Probability (0.0-1.0) of returning an error response
140
+ - **`errorStatus`** (number): HTTP status code for simulated errors
141
+ - **`errorResponse`** (any): Response body when error is triggered (supports templates)
142
+ - **`fakerSeed`** (number, optional): Seed for deterministic faker data generation
143
+ - **`cors`** (object, optional): CORS configuration
144
+ - `origin` (string | string[] | boolean): Allowed origins (`"*"`, `"https://example.com"`, or array)
145
+ - `credentials` (boolean): Allow credentials
146
+ - `methods` (string[]): Allowed HTTP methods
147
+ - `allowedHeaders` (string[]): Allowed request headers
148
+ - `exposedHeaders` (string[]): Exposed response headers
149
+ - `maxAge` (number): Preflight cache duration in seconds
150
+
151
+ ### Endpoints
152
+
153
+ Each endpoint defines a route with its matching rules and response behavior:
154
+
155
+ ```json
156
+ {
157
+ "method": "GET",
158
+ "path": "/users/:id",
159
+ "match": {
160
+ "query": { "type": "premium" },
161
+ "headers": { "Authorization": "Bearer token123" },
162
+ "cookies": { "sessionId": "valid" }
163
+ },
164
+ "status": 200,
165
+ "response": { ... },
166
+ "headers": {
167
+ "X-Custom-Header": "value",
168
+ "Cache-Control": "no-cache"
169
+ },
170
+ "delay": { "min": 100, "max": 500 },
171
+ "delayMs": 0,
172
+ "errorRate": 0,
173
+ "errorStatus": 500,
174
+ "errorResponse": { "error": "Not found" },
175
+ "variants": [ ... ]
176
+ }
177
+ ```
178
+
179
+ #### Endpoint Fields
180
+
181
+ - **`method`** (string): HTTP method - `GET`, `POST`, `PUT`, `PATCH`, `DELETE`
182
+ - **`path`** (string): Route path with Fastify-style params (e.g., `/users/:id`)
183
+ - **`match`** (object, optional): Request matching rules (see [Match Rules](#match-rules))
184
+ - **`status`** (number): Default HTTP status code (default: `200`)
185
+ - **`response`** (any): Response body (supports templates)
186
+ - **`headers`** (object, optional): Custom response headers (supports templates in values)
187
+ - **`delay`** (object, optional): Variable delay range - `{ "min": 100, "max": 500 }`
188
+ - **`delayMs`** (number, optional): Fixed delay in milliseconds
189
+ - **`errorRate`** (number, optional): Error probability override
190
+ - **`errorStatus`** (number, optional): Error status override
191
+ - **`errorResponse`** (any, optional): Error response override
192
+ - **`variants`** (array, optional): Alternative responses with their own match rules
193
+
194
+ ### Match Rules
195
+
196
+ Match rules determine if a request should be handled by an endpoint or variant. All specified match conditions must be satisfied.
197
+
198
+ ```json
199
+ "match": {
200
+ "query": { "type": "premium", "status": "active" },
201
+ "body": { "email": "test@example.com" },
202
+ "headers": { "Authorization": "Bearer secret" },
203
+ "cookies": { "sessionId": "abc123" }
204
+ }
205
+ ```
206
+
207
+ #### Match Fields
208
+
209
+ - **`query`** (object): Exact match for query parameters (strings, numbers, booleans)
210
+ - **`body`** (object): Exact match for top-level body fields
211
+ - **`headers`** (object): Case-insensitive exact match for headers
212
+ - **`cookies`** (object): Exact match for cookies
213
+
214
+ If a request doesn't satisfy the match rules, the server returns `404` with:
215
+ ```json
216
+ { "error": "No matching mock for request" }
217
+ ```
218
+
219
+ ### Variants
220
+
221
+ Variants provide alternative responses based on match rules. The **first matching variant wins**. If no variant matches, the endpoint's base response is used.
222
+
223
+ ```json
224
+ "variants": [
225
+ {
226
+ "name": "admin user",
227
+ "match": { "body": { "role": "admin" } },
228
+ "status": 200,
229
+ "response": { "ok": true, "role": "admin", "permissions": ["*"] },
230
+ "headers": { "X-User-Role": "admin" }
231
+ },
232
+ {
233
+ "name": "invalid credentials",
234
+ "match": { "body": { "password": "wrong" } },
235
+ "status": 401,
236
+ "response": { "ok": false, "error": "Invalid credentials" }
237
+ }
238
+ ]
239
+ ```
240
+
241
+ #### Variant Fields
242
+
243
+ - **`name`** (string, optional): Descriptive name for the variant
244
+ - **`match`** (object, optional): Match rules (same as endpoint-level)
245
+ - **`status`** (number, optional): HTTP status code
246
+ - **`response`** (any): Response body (supports templates)
247
+ - **`headers`** (object, optional): Custom response headers (overrides endpoint headers)
248
+ - **`delay`** / **`delayMs`** (optional): Delay overrides
249
+ - **`errorRate`**, **`errorStatus`**, **`errorResponse`** (optional): Error simulation overrides
250
+
251
+ ### Response Templates
252
+
253
+ Responses support a powerful templating system combining static values, request data, faker directives, and array generation.
254
+
255
+ #### String Placeholders
256
+
257
+ Access request data using mustache-style placeholders:
258
+
259
+ ```json
260
+ {
261
+ "userId": "{{params.id}}",
262
+ "searchType": "{{query.type}}",
263
+ "userEmail": "{{body.email}}"
264
+ }
265
+ ```
266
+
267
+ **Available contexts:**
268
+ - `{{params.name}}` - Path parameters
269
+ - `{{query.key}}` - Query string parameters
270
+ - `{{body.field}}` - Request body fields (supports nested paths like `{{body.user.name}}`)
271
+
272
+ #### Faker Directives
273
+
274
+ Generate realistic fake data using any `@faker-js/faker` method:
275
+
276
+ **Simple syntax:**
277
+ ```json
278
+ {
279
+ "name": { "__faker": "person.fullName" },
280
+ "email": { "__faker": "internet.email" },
281
+ "phone": { "__faker": "phone.number" },
282
+ "company": { "__faker": "company.name" },
283
+ "avatar": { "__faker": "image.avatar" },
284
+ "birthdate": { "__faker": "date.birthdate" },
285
+ "city": { "__faker": "location.city" }
286
+ }
287
+ ```
288
+
289
+ **With arguments:**
290
+ ```json
291
+ {
292
+ "randomString": { "__faker": { "method": "string.alpha", "args": [16] } },
293
+ "price": { "__faker": { "method": "number.float", "args": [{ "min": 10, "max": 100, "precision": 0.01 }] } }
294
+ }
295
+ ```
296
+
297
+ **Deterministic output:**
298
+ Set `fakerSeed` in settings for consistent data across requests:
299
+ ```json
300
+ {
301
+ "settings": {
302
+ "fakerSeed": 12345
303
+ }
304
+ }
305
+ ```
306
+
307
+ #### Repeat Directives
308
+
309
+ Generate arrays of items with random or fixed counts:
310
+
311
+ **Random count (min/max range):**
312
+ ```json
313
+ {
314
+ "users": {
315
+ "__repeat": {
316
+ "min": 10,
317
+ "max": 15,
318
+ "template": {
319
+ "id": { "__faker": "string.uuid" },
320
+ "name": { "__faker": "person.fullName" },
321
+ "email": { "__faker": "internet.email" }
322
+ }
323
+ }
324
+ }
325
+ }
326
+ ```
327
+
328
+ **Fixed count:**
329
+ ```json
330
+ {
331
+ "tags": {
332
+ "__repeat": {
333
+ "count": 3,
334
+ "template": { "__faker": "lorem.word" }
335
+ }
336
+ }
337
+ }
338
+ ```
339
+
340
+ **Combining templates:**
341
+ ```json
342
+ {
343
+ "userId": "{{params.id}}",
344
+ "orders": {
345
+ "__repeat": {
346
+ "min": 5,
347
+ "max": 10,
348
+ "template": {
349
+ "orderId": { "__faker": "string.uuid" },
350
+ "amount": { "__faker": { "method": "number.float", "args": [{ "min": 10, "max": 1000, "precision": 0.01 }] } },
351
+ "status": { "__faker": { "method": "helpers.arrayElement", "args": [["pending", "shipped", "delivered"]] } }
352
+ }
353
+ }
354
+ }
355
+ }
356
+ ```
357
+
358
+ ## Advanced Features
359
+
360
+ ### Custom Response Headers
361
+
362
+ Set custom headers on responses, with support for templating:
363
+
364
+ ```json
365
+ {
366
+ "method": "GET",
367
+ "path": "/api/data/:id",
368
+ "response": { "data": "value" },
369
+ "headers": {
370
+ "X-Resource-ID": "{{params.id}}",
371
+ "X-Request-Type": "{{query.type}}",
372
+ "Cache-Control": "max-age=3600",
373
+ "X-Custom-Header": "static-value"
374
+ }
375
+ }
376
+ ```
377
+
378
+ Variant headers override endpoint headers:
379
+
380
+ ```json
381
+ {
382
+ "method": "GET",
383
+ "path": "/api/data",
384
+ "headers": { "X-Source": "base" },
385
+ "variants": [
386
+ {
387
+ "match": { "query": { "premium": "true" } },
388
+ "response": { "data": "premium" },
389
+ "headers": { "X-Source": "premium", "X-Tier": "gold" }
390
+ }
391
+ ]
392
+ }
393
+ ```
394
+
395
+ ### Variable Delays
396
+
397
+ Simulate realistic network latency with random delay ranges:
398
+
399
+ ```json
400
+ {
401
+ "method": "GET",
402
+ "path": "/api/slow",
403
+ "delay": { "min": 100, "max": 500 },
404
+ "response": { "ok": true }
405
+ }
406
+ ```
407
+
408
+ The server will wait a random duration between 100ms and 500ms before responding.
409
+
410
+ ### Request History
411
+
412
+ All requests are automatically recorded and can be inspected via the history endpoint:
413
+
414
+ **View all requests:**
415
+ ```bash
416
+ GET /__history
417
+ ```
418
+
419
+ **Filter by endpoint:**
420
+ ```bash
421
+ GET /__history?endpoint=/api/users
422
+ ```
423
+
424
+ **Filter by method:**
425
+ ```bash
426
+ GET /__history?method=POST
427
+ ```
428
+
429
+ **Limit results:**
430
+ ```bash
431
+ GET /__history?limit=10
432
+ ```
433
+
434
+ **Clear history:**
435
+ ```bash
436
+ DELETE /__history
437
+ ```
438
+
439
+ **History entry format:**
440
+ ```json
441
+ {
442
+ "entries": [
443
+ {
444
+ "id": "uuid",
445
+ "timestamp": "2026-01-18T14:30:00.000Z",
446
+ "method": "POST",
447
+ "url": "/api/users",
448
+ "path": "/api/users",
449
+ "query": {},
450
+ "headers": { "content-type": "application/json" },
451
+ "body": { "name": "John" },
452
+ "statusCode": 201,
453
+ "responseTime": 45
454
+ }
455
+ ],
456
+ "total": 1
457
+ }
458
+ ```
459
+
460
+ ### CORS Configuration
461
+
462
+ Enable CORS globally or per-endpoint:
463
+
464
+ **Global CORS:**
465
+ ```json
466
+ {
467
+ "settings": {
468
+ "cors": {
469
+ "origin": "*",
470
+ "credentials": true,
471
+ "methods": ["GET", "POST", "PUT", "DELETE"],
472
+ "allowedHeaders": ["Content-Type", "Authorization"]
473
+ }
474
+ }
475
+ }
476
+ ```
477
+
478
+ **Per-endpoint CORS:**
479
+ ```json
480
+ {
481
+ "method": "GET",
482
+ "path": "/api/public",
483
+ "cors": { "origin": "https://example.com" },
484
+ "response": { "data": "public" }
485
+ }
486
+ ```
487
+
488
+ ### Error Simulation
489
+
490
+ Simulate random errors for reliability testing:
491
+
492
+ **Global error rate:**
493
+ ```json
494
+ {
495
+ "settings": {
496
+ "errorRate": 0.1,
497
+ "errorStatus": 503,
498
+ "errorResponse": { "error": "Service temporarily unavailable" }
499
+ }
500
+ }
501
+ ```
502
+
503
+ **Per-endpoint error rate:**
504
+ ```json
505
+ {
506
+ "method": "GET",
507
+ "path": "/api/unstable",
508
+ "errorRate": 0.5,
509
+ "errorStatus": 500,
510
+ "errorResponse": { "error": "Internal server error" },
511
+ "response": { "ok": true }
512
+ }
513
+ ```
514
+
515
+ 10% of requests will return the error response with the specified status code.
516
+
517
+ ## Built-in Endpoints
518
+
519
+ mockserve provides several special endpoints for inspection and debugging:
520
+
521
+ - **`GET /health`** - Health check endpoint (returns `{ "ok": true }`)
522
+ - **`GET /__spec`** - View the loaded spec and metadata
523
+ - **`GET /__openapi.json`** - OpenAPI 3.0 specification in JSON format
524
+ - **`GET /__openapi.yaml`** - OpenAPI 3.0 specification in YAML format
525
+ - **`GET /docs`** - Interactive Swagger UI documentation
526
+ - **`GET /__history`** - View request history (supports filtering)
527
+ - **`DELETE /__history`** - Clear request history
528
+
529
+ ## Examples
530
+
531
+ The `examples/` folder contains ready-to-use spec files demonstrating various features:
532
+
533
+ 1. **`basic-crud.json`** - Simple CRUD operations with templating
534
+ 2. **`auth-variants.json`** - Authentication with variants for different scenarios
535
+ 3. **`users-faker.json`** - User list with faker-generated data and array ranges
536
+ 4. **`companies-nested.json`** - Nested data structures with companies and employees
537
+ 5. **`orders-and-matches.json`** - Complex matching with headers, cookies, and query params
538
+
539
+ Run any example:
540
+ ```bash
541
+ mockserve serve --spec examples/users-faker.json
542
+ ```
543
+
544
+ ## Use Cases
545
+
546
+ ### Development
547
+
548
+ Replace real backends during frontend development:
549
+ - No backend dependencies
550
+ - Instant API responses
551
+ - Test edge cases easily with variants
552
+ - Simulate network conditions with delays
553
+
554
+ ### Testing
555
+
556
+ Create reliable, repeatable test environments:
557
+ - Deterministic data with `fakerSeed`
558
+ - Test error scenarios with `errorRate`
559
+ - Validate request/response flow with history
560
+ - Multiple test scenarios with variants
561
+
562
+ ### Documentation
563
+
564
+ Auto-generated interactive API docs:
565
+ - OpenAPI/Swagger UI out of the box
566
+ - View all endpoints and response examples
567
+ - Test endpoints directly in the browser
568
+ - Export OpenAPI spec for tooling
569
+
570
+ ### Demos & Prototypes
571
+
572
+ Quickly mock APIs for demos and prototypes:
573
+ - No coding required - just JSON
574
+ - Realistic data with faker
575
+ - Professional-looking APIs
576
+ - Easy to modify and iterate
577
+
578
+ ## Schema Validation
579
+
580
+ Your spec file is validated against `mockserve.spec.schema.json`. Use JSON schema validation in your editor (VS Code, WebStorm, etc.) for autocomplete and error checking.
581
+
582
+ Add this to your spec file:
583
+ ```json
584
+ {
585
+ "$schema": "./mockserve.spec.schema.json",
586
+ "version": 1,
587
+ ...
588
+ }
589
+ ```
590
+
591
+ ## Development
592
+
593
+ ```bash
594
+ # Install dependencies
595
+ npm install
596
+
597
+ # Run in development mode
598
+ npm run dev
599
+
600
+ # Build
601
+ npm run build
602
+
603
+ # Run tests
604
+ npm test
605
+
606
+ # Watch tests
607
+ npm run test:watch
608
+
609
+ # Generate JSON schema from Zod spec
610
+ npm run schema:build
611
+ ```
612
+
613
+ ## Architecture
614
+
615
+ - **TypeScript** - Fully typed codebase with strict mode
616
+ - **Fastify** - Fast, low-overhead web framework
617
+ - **Zod** - Runtime schema validation
618
+ - **Faker** - Realistic fake data generation
619
+ - **Pino** - High-performance logging
620
+ - **Vitest** - Fast unit testing
621
+
622
+ ## License
623
+
624
+ ISC
625
+
626
+ ## Contributing
627
+
628
+ Contributions are welcome! Please ensure:
629
+ - All tests pass (`npm test`)
630
+ - Code is properly typed (no `any`)
631
+ - Functions have JSDoc comments
632
+ - New features include tests and documentation
633
+
634
+ ---
635
+
636
+ **Made with ❤️ for developers who need reliable mock APIs**
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveDelay = resolveDelay;
4
+ exports.sleep = sleep;
5
+ exports.shouldFail = shouldFail;
6
+ exports.resolveBehavior = resolveBehavior;
7
+ const faker_1 = require("@faker-js/faker");
8
+ /**
9
+ * Resolve a delay value from either a number or a range configuration.
10
+ */
11
+ function resolveDelay(delay) {
12
+ if (!delay)
13
+ return 0;
14
+ if (typeof delay === "number")
15
+ return delay;
16
+ return faker_1.faker.number.int({ min: delay.min, max: delay.max });
17
+ }
18
+ /**
19
+ * Pause for the given number of milliseconds.
20
+ */
21
+ function sleep(ms) {
22
+ return new Promise((resolve) => setTimeout(resolve, ms));
23
+ }
24
+ /**
25
+ * Decide whether a request should fail based on an error rate.
26
+ */
27
+ function shouldFail(errorRate) {
28
+ if (errorRate <= 0)
29
+ return false;
30
+ if (errorRate >= 1)
31
+ return true;
32
+ return Math.random() < errorRate;
33
+ }
34
+ /**
35
+ * Resolve behavior settings with precedence: chosen overrides -> endpoint overrides -> global settings.
36
+ */
37
+ function resolveBehavior(settings, endpointOverrides, chosenOverrides) {
38
+ return {
39
+ delayMs: chosenOverrides?.delayMs ?? endpointOverrides?.delayMs ?? settings.delayMs,
40
+ errorRate: chosenOverrides?.errorRate ?? endpointOverrides?.errorRate ?? settings.errorRate,
41
+ errorStatus: chosenOverrides?.errorStatus ?? endpointOverrides?.errorStatus ?? settings.errorStatus,
42
+ errorResponse: chosenOverrides?.errorResponse ?? endpointOverrides?.errorResponse ?? settings.errorResponse
43
+ };
44
+ }