@zze/mock-server 0.2.6 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +117 -28
- package/dist/__tests__/mock-server.test.js +58 -1
- package/dist/admin/__tests__/admin-router.test.js +2 -151
- package/dist/admin/admin-router.d.ts +4 -4
- package/dist/admin/admin-router.d.ts.map +1 -1
- package/dist/admin/admin-router.js +1 -205
- package/dist/admin/types.d.ts +1 -84
- package/dist/admin/types.d.ts.map +1 -1
- package/dist/admin/types.js +1 -5
- package/dist/config/__tests__/config-loader.test.js +18 -0
- package/dist/config/config-loader.d.ts +3 -3
- package/dist/config/config-loader.d.ts.map +1 -1
- package/dist/config/config-loader.js +4 -1
- package/dist/config/config-writer.d.ts +1 -35
- package/dist/config/config-writer.d.ts.map +1 -1
- package/dist/config/config-writer.js +0 -103
- package/dist/config/index.d.ts +1 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/types.d.ts +9 -1
- package/dist/config/types.d.ts.map +1 -1
- package/dist/mock-server.d.ts +9 -0
- package/dist/mock-server.d.ts.map +1 -1
- package/dist/mock-server.js +3 -0
- package/dist/resolver/__tests__/response-resolver.test.js +134 -0
- package/dist/resolver/response-resolver.d.ts +18 -2
- package/dist/resolver/response-resolver.d.ts.map +1 -1
- package/dist/resolver/response-resolver.js +62 -5
- package/dist/resolver/types.d.ts +8 -0
- package/dist/resolver/types.d.ts.map +1 -1
- package/dist/resolver/types.js +7 -0
- package/dist/schemas/__tests__/endpoint.schema.test.js +48 -0
- package/dist/schemas/endpoint.schema.d.ts +31 -8
- package/dist/schemas/endpoint.schema.d.ts.map +1 -1
- package/dist/schemas/scenario.schema.d.ts +28 -4
- package/dist/schemas/scenario.schema.d.ts.map +1 -1
- package/dist/schemas/scenario.schema.js +23 -3
- package/dist/server/__tests__/mock-server-app.test.js +40 -4
- package/dist/server/mock-server-app.d.ts +4 -4
- package/dist/server/mock-server-app.d.ts.map +1 -1
- package/dist/server/mock-server-app.js +5 -3
- package/dist/server/types.d.ts +8 -0
- package/dist/server/types.d.ts.map +1 -1
- package/dist/server/types.js +1 -0
- package/dist/state/__tests__/state-manager.test.js +33 -0
- package/dist/state/state-manager.d.ts.map +1 -1
- package/dist/state/state-manager.js +9 -6
- package/dist/ui/assets/index-C2lhc6Pd.css +1 -0
- package/dist/ui/assets/index-D8nVOwBC.js +9 -0
- package/dist/ui/index.html +2 -2
- package/package.json +5 -3
- package/dist/ui/assets/index-SADMOgPE.js +0 -10
- package/dist/ui/assets/index-rj7m-XdG.css +0 -1
package/dist/config/types.d.ts
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
import type { EndpointConfig, FlowConfig } from '../schemas/index.js';
|
|
2
|
+
export type RuntimeEndpointConfig = EndpointConfig & {
|
|
3
|
+
sourceFile?: string;
|
|
4
|
+
};
|
|
5
|
+
/** Validated endpoint config with loader-derived metadata */
|
|
6
|
+
export type LoadedEndpointConfig = RuntimeEndpointConfig & {
|
|
7
|
+
/** Path relative to the endpoints directory, normalized for API consumers */
|
|
8
|
+
sourceFile: string;
|
|
9
|
+
};
|
|
2
10
|
/**
|
|
3
11
|
* Options for initializing the MockServer
|
|
4
12
|
*/
|
|
@@ -16,7 +24,7 @@ export interface MockServerOptions {
|
|
|
16
24
|
* Loaded configuration from disk
|
|
17
25
|
*/
|
|
18
26
|
export interface LoadedConfig {
|
|
19
|
-
endpoints:
|
|
27
|
+
endpoints: LoadedEndpointConfig[];
|
|
20
28
|
flows: FlowConfig[];
|
|
21
29
|
}
|
|
22
30
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/config/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEtE;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,8DAA8D;IAC9D,UAAU,EAAE,MAAM,CAAC;IACnB,gDAAgD;IAChD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,+DAA+D;IAC/D,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/config/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEtE,MAAM,MAAM,qBAAqB,GAAG,cAAc,GAAG;IACnD,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,6DAA6D;AAC7D,MAAM,MAAM,oBAAoB,GAAG,qBAAqB,GAAG;IACzD,6EAA6E;IAC7E,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,8DAA8D;IAC9D,UAAU,EAAE,MAAM,CAAC;IACnB,gDAAgD;IAChD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,+DAA+D;IAC/D,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,oBAAoB,EAAE,CAAC;IAClC,KAAK,EAAE,UAAU,EAAE,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc,CAAC,CAAC;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,CAAC,CAAC;IACT,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,MAAM,EAAE,KAAK,CAAC;QACZ,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE,MAAM,CAAC;KACf,CAAC,CAAC;CACJ;AAED;;GAEG;AACH,eAAO,MAAM,oBAAoB,kDAAmD,CAAC;AAErF,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAC"}
|
package/dist/mock-server.d.ts
CHANGED
|
@@ -38,6 +38,15 @@ export interface MockServerOptions {
|
|
|
38
38
|
* Options for the file watcher (only used when hotReload is true).
|
|
39
39
|
*/
|
|
40
40
|
watcherOptions?: WatcherOptions;
|
|
41
|
+
/**
|
|
42
|
+
* Global delay applied to every response when the matched scenario has no explicit `delay`.
|
|
43
|
+
* A number is a fixed millisecond value; a tuple [min, max] produces a random value in that range.
|
|
44
|
+
* Scenario-level `delay` always takes precedence.
|
|
45
|
+
* @example
|
|
46
|
+
* globalDelay: 200 // every response waits 200 ms (unless scenario overrides)
|
|
47
|
+
* globalDelay: [100, 500] // each response waits a random 100-500 ms
|
|
48
|
+
*/
|
|
49
|
+
globalDelay?: number | [number, number];
|
|
41
50
|
}
|
|
42
51
|
/**
|
|
43
52
|
* Current state of the mock server
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mock-server.d.ts","sourceRoot":"","sources":["../src/mock-server.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAOH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AACnC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAGvC;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAC;IAEnB;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB;;OAEG;IACH,cAAc,CAAC,EAAE,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"mock-server.d.ts","sourceRoot":"","sources":["../src/mock-server.ts"],"names":[],"mappings":"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"}
|
package/dist/mock-server.js
CHANGED
|
@@ -57,6 +57,7 @@ class MockServer {
|
|
|
57
57
|
apiPrefix: options.apiPrefix ?? '/api',
|
|
58
58
|
hotReload: options.hotReload ?? true,
|
|
59
59
|
watcherOptions: options.watcherOptions,
|
|
60
|
+
globalDelay: options.globalDelay,
|
|
60
61
|
};
|
|
61
62
|
// Create config loader
|
|
62
63
|
this.configLoader = new config_loader_js_1.ConfigLoader(this.options.configPath);
|
|
@@ -85,6 +86,8 @@ class MockServer {
|
|
|
85
86
|
port: this.options.port,
|
|
86
87
|
apiPrefix: this.options.apiPrefix,
|
|
87
88
|
hotReload: this.options.hotReload,
|
|
89
|
+
configPath: this.options.configPath,
|
|
90
|
+
globalDelay: this.options.globalDelay,
|
|
88
91
|
};
|
|
89
92
|
this.serverApp = new mock_server_app_js_1.MockServerApp(serverOptions);
|
|
90
93
|
// Set config writer for endpoint/flow management via admin API
|
|
@@ -355,4 +355,138 @@ const mockEndpoints = [
|
|
|
355
355
|
const resolver = (0, response_resolver_js_1.createResponseResolver)(stateManager);
|
|
356
356
|
(0, vitest_1.expect)(resolver).toBeInstanceOf(response_resolver_js_1.ResponseResolver);
|
|
357
357
|
});
|
|
358
|
+
(0, vitest_1.it)('should accept optional configPath', () => {
|
|
359
|
+
const stateManager = new index_js_1.StateManager();
|
|
360
|
+
const resolver = (0, response_resolver_js_1.createResponseResolver)(stateManager, '/some/path');
|
|
361
|
+
(0, vitest_1.expect)(resolver).toBeInstanceOf(response_resolver_js_1.ResponseResolver);
|
|
362
|
+
});
|
|
363
|
+
});
|
|
364
|
+
(0, vitest_1.describe)('file response', () => {
|
|
365
|
+
let stateManager;
|
|
366
|
+
let resolver;
|
|
367
|
+
const fileEndpoint = {
|
|
368
|
+
id: 'download-report',
|
|
369
|
+
path: '/reports/report.pdf',
|
|
370
|
+
method: 'GET',
|
|
371
|
+
scenarios: [
|
|
372
|
+
{
|
|
373
|
+
id: 'pdf',
|
|
374
|
+
name: 'PDF Report',
|
|
375
|
+
status: 200,
|
|
376
|
+
file: './fixtures/report.pdf',
|
|
377
|
+
},
|
|
378
|
+
],
|
|
379
|
+
defaultScenarioId: 'pdf',
|
|
380
|
+
};
|
|
381
|
+
const jsonEndpoint = {
|
|
382
|
+
id: 'get-data',
|
|
383
|
+
path: '/data',
|
|
384
|
+
method: 'GET',
|
|
385
|
+
scenarios: [{ id: 'success', name: 'Success', status: 200, body: { ok: true } }],
|
|
386
|
+
defaultScenarioId: 'success',
|
|
387
|
+
};
|
|
388
|
+
(0, vitest_1.beforeEach)(() => {
|
|
389
|
+
stateManager = new index_js_1.StateManager();
|
|
390
|
+
stateManager.setEndpoints([fileEndpoint, jsonEndpoint]);
|
|
391
|
+
resolver = (0, response_resolver_js_1.createResponseResolver)(stateManager, '/config/path');
|
|
392
|
+
resolver.setEndpoints([fileEndpoint, jsonEndpoint]);
|
|
393
|
+
});
|
|
394
|
+
(0, vitest_1.it)('should resolve filePath in ResolvedResponse when scenario has file', () => {
|
|
395
|
+
const req = createMockRequest();
|
|
396
|
+
const resolved = resolver.resolveResponse('download-report', req);
|
|
397
|
+
(0, vitest_1.expect)(resolved).not.toBeNull();
|
|
398
|
+
(0, vitest_1.expect)(resolved.filePath).toBe('/config/path/fixtures/report.pdf');
|
|
399
|
+
(0, vitest_1.expect)(resolved.body).toBeUndefined();
|
|
400
|
+
});
|
|
401
|
+
(0, vitest_1.it)('should update filePath when configPath changes via setConfigPath', () => {
|
|
402
|
+
resolver.setConfigPath('/new/config');
|
|
403
|
+
const req = createMockRequest();
|
|
404
|
+
const resolved = resolver.resolveResponse('download-report', req);
|
|
405
|
+
(0, vitest_1.expect)(resolved.filePath).toBe('/new/config/fixtures/report.pdf');
|
|
406
|
+
});
|
|
407
|
+
(0, vitest_1.it)('should not set filePath for JSON scenarios', () => {
|
|
408
|
+
const req = createMockRequest();
|
|
409
|
+
const resolved = resolver.resolveResponse('get-data', req);
|
|
410
|
+
(0, vitest_1.expect)(resolved.filePath).toBeUndefined();
|
|
411
|
+
});
|
|
412
|
+
});
|
|
413
|
+
(0, vitest_1.describe)('resolveDelay', () => {
|
|
414
|
+
(0, vitest_1.it)('should return scenario delay when defined', () => {
|
|
415
|
+
(0, vitest_1.expect)((0, response_resolver_js_1.resolveDelay)(200)).toBe(200);
|
|
416
|
+
});
|
|
417
|
+
(0, vitest_1.it)('should return scenario delay even when globalDelay is also set', () => {
|
|
418
|
+
(0, vitest_1.expect)((0, response_resolver_js_1.resolveDelay)(50, 500)).toBe(50);
|
|
419
|
+
(0, vitest_1.expect)((0, response_resolver_js_1.resolveDelay)(0, 500)).toBe(0);
|
|
420
|
+
});
|
|
421
|
+
(0, vitest_1.it)('should return 0 when neither scenario delay nor globalDelay is set', () => {
|
|
422
|
+
(0, vitest_1.expect)((0, response_resolver_js_1.resolveDelay)(undefined)).toBe(0);
|
|
423
|
+
(0, vitest_1.expect)((0, response_resolver_js_1.resolveDelay)(undefined, undefined)).toBe(0);
|
|
424
|
+
});
|
|
425
|
+
(0, vitest_1.it)('should return static globalDelay when scenario has no delay', () => {
|
|
426
|
+
(0, vitest_1.expect)((0, response_resolver_js_1.resolveDelay)(undefined, 300)).toBe(300);
|
|
427
|
+
});
|
|
428
|
+
(0, vitest_1.it)('should return a value within [min, max] range when globalDelay is a tuple', () => {
|
|
429
|
+
const results = Array.from({ length: 50 }, () => (0, response_resolver_js_1.resolveDelay)(undefined, [100, 200]));
|
|
430
|
+
for (const r of results) {
|
|
431
|
+
(0, vitest_1.expect)(r).toBeGreaterThanOrEqual(100);
|
|
432
|
+
(0, vitest_1.expect)(r).toBeLessThanOrEqual(200);
|
|
433
|
+
}
|
|
434
|
+
});
|
|
435
|
+
});
|
|
436
|
+
(0, vitest_1.describe)('ResponseResolver globalDelay', () => {
|
|
437
|
+
const endpoint = {
|
|
438
|
+
id: 'ep',
|
|
439
|
+
path: '/ep',
|
|
440
|
+
method: 'GET',
|
|
441
|
+
scenarios: [
|
|
442
|
+
{ id: 'no-delay', name: 'No delay', status: 200, body: 'ok' },
|
|
443
|
+
{ id: 'with-delay', name: 'With delay', status: 200, body: 'ok', delay: 10 },
|
|
444
|
+
],
|
|
445
|
+
defaultScenarioId: 'no-delay',
|
|
446
|
+
};
|
|
447
|
+
(0, vitest_1.it)('should apply static globalDelay when scenario has no delay', () => {
|
|
448
|
+
const sm = new index_js_1.StateManager();
|
|
449
|
+
sm.setEndpoints([endpoint]);
|
|
450
|
+
const resolver = (0, response_resolver_js_1.createResponseResolver)(sm, '', 150);
|
|
451
|
+
resolver.setEndpoints([endpoint]);
|
|
452
|
+
const resolved = resolver.resolveResponse('ep', createMockRequest());
|
|
453
|
+
(0, vitest_1.expect)(resolved.delay).toBe(150);
|
|
454
|
+
});
|
|
455
|
+
(0, vitest_1.it)('should apply a range globalDelay within bounds', () => {
|
|
456
|
+
const sm = new index_js_1.StateManager();
|
|
457
|
+
sm.setEndpoints([endpoint]);
|
|
458
|
+
const resolver = (0, response_resolver_js_1.createResponseResolver)(sm, '', [50, 100]);
|
|
459
|
+
resolver.setEndpoints([endpoint]);
|
|
460
|
+
for (let i = 0; i < 20; i++) {
|
|
461
|
+
const resolved = resolver.resolveResponse('ep', createMockRequest());
|
|
462
|
+
(0, vitest_1.expect)(resolved.delay).toBeGreaterThanOrEqual(50);
|
|
463
|
+
(0, vitest_1.expect)(resolved.delay).toBeLessThanOrEqual(100);
|
|
464
|
+
}
|
|
465
|
+
});
|
|
466
|
+
(0, vitest_1.it)('should use scenario delay over globalDelay when scenario has delay set', () => {
|
|
467
|
+
const sm = new index_js_1.StateManager();
|
|
468
|
+
sm.setEndpoints([endpoint]);
|
|
469
|
+
sm.activateScenario('ep', 'with-delay');
|
|
470
|
+
const resolver = (0, response_resolver_js_1.createResponseResolver)(sm, '', 999);
|
|
471
|
+
resolver.setEndpoints([endpoint]);
|
|
472
|
+
const resolved = resolver.resolveResponse('ep', createMockRequest());
|
|
473
|
+
(0, vitest_1.expect)(resolved.delay).toBe(10);
|
|
474
|
+
});
|
|
475
|
+
(0, vitest_1.it)('should default to 0 delay when no scenario delay and no globalDelay', () => {
|
|
476
|
+
const sm = new index_js_1.StateManager();
|
|
477
|
+
sm.setEndpoints([endpoint]);
|
|
478
|
+
const resolver = (0, response_resolver_js_1.createResponseResolver)(sm);
|
|
479
|
+
resolver.setEndpoints([endpoint]);
|
|
480
|
+
const resolved = resolver.resolveResponse('ep', createMockRequest());
|
|
481
|
+
(0, vitest_1.expect)(resolved.delay).toBe(0);
|
|
482
|
+
});
|
|
483
|
+
(0, vitest_1.it)('should update globalDelay via setGlobalDelay', () => {
|
|
484
|
+
const sm = new index_js_1.StateManager();
|
|
485
|
+
sm.setEndpoints([endpoint]);
|
|
486
|
+
const resolver = (0, response_resolver_js_1.createResponseResolver)(sm);
|
|
487
|
+
resolver.setEndpoints([endpoint]);
|
|
488
|
+
resolver.setGlobalDelay(250);
|
|
489
|
+
const resolved = resolver.resolveResponse('ep', createMockRequest());
|
|
490
|
+
(0, vitest_1.expect)(resolved.delay).toBe(250);
|
|
491
|
+
});
|
|
358
492
|
});
|
|
@@ -10,7 +10,17 @@ export declare class ResponseResolver {
|
|
|
10
10
|
private stateManager;
|
|
11
11
|
private endpoints;
|
|
12
12
|
private routeMap;
|
|
13
|
-
|
|
13
|
+
private configPath;
|
|
14
|
+
private globalDelay;
|
|
15
|
+
constructor(stateManager: StateManager, configPath?: string, globalDelay?: number | [number, number]);
|
|
16
|
+
/**
|
|
17
|
+
* Update the config path (used when config is reloaded from a new location)
|
|
18
|
+
*/
|
|
19
|
+
setConfigPath(configPath: string): void;
|
|
20
|
+
/**
|
|
21
|
+
* Update the global delay at runtime
|
|
22
|
+
*/
|
|
23
|
+
setGlobalDelay(globalDelay: number | [number, number] | undefined): void;
|
|
14
24
|
/**
|
|
15
25
|
* Register endpoints for routing
|
|
16
26
|
*/
|
|
@@ -66,8 +76,14 @@ export declare class ResponseResolver {
|
|
|
66
76
|
*/
|
|
67
77
|
handleRequest(endpointId: string, req: Request, res: Response): Promise<boolean>;
|
|
68
78
|
}
|
|
79
|
+
/**
|
|
80
|
+
* Resolve the effective delay for a response.
|
|
81
|
+
* The scenario's own delay (when defined) always wins over the global fallback.
|
|
82
|
+
* A globalDelay tuple [min, max] produces a uniformly distributed random value.
|
|
83
|
+
*/
|
|
84
|
+
export declare function resolveDelay(scenarioDelay: number | undefined, globalDelay?: number | [number, number]): number;
|
|
69
85
|
/**
|
|
70
86
|
* Create a new ResponseResolver instance
|
|
71
87
|
*/
|
|
72
|
-
export declare function createResponseResolver(stateManager: StateManager): ResponseResolver;
|
|
88
|
+
export declare function createResponseResolver(stateManager: StateManager, configPath?: string, globalDelay?: number | [number, number]): ResponseResolver;
|
|
73
89
|
//# sourceMappingURL=response-resolver.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"response-resolver.d.ts","sourceRoot":"","sources":["../../src/resolver/response-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"response-resolver.d.ts","sourceRoot":"","sources":["../../src/resolver/response-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEjD,OAAO,KAAK,EAAE,cAAc,EAAkB,MAAM,qBAAqB,CAAC;AAC1E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAiB,MAAM,YAAY,CAAC;AAGlF;;;GAGG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,SAAS,CAA0C;IAC3D,OAAO,CAAC,QAAQ,CAA0C;IAC1D,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,WAAW,CAAwC;gBAGzD,YAAY,EAAE,YAAY,EAC1B,UAAU,GAAE,MAAW,EACvB,WAAW,CAAC,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC;IAOzC;;OAEG;IACH,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAIvC;;OAEG;IACH,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,GAAG,IAAI;IAIxE;;OAEG;IACH,YAAY,CAAC,SAAS,EAAE,cAAc,EAAE,GAAG,IAAI;IAY/C;;OAEG;IACH,OAAO,CAAC,cAAc;IAItB;;OAEG;IACH,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI;IAKjE;;OAEG;IACH,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI;IAI1D;;OAEG;IACH,YAAY,IAAI,cAAc,EAAE;IAIhC;;;OAGG;IACH,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,GAAG,gBAAgB,GAAG,IAAI;IAc1E;;OAEG;IACH,sBAAsB,CACpB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,OAAO,GACX,gBAAgB,GAAG,IAAI;IAS1B;;OAEG;IACH,OAAO,CAAC,aAAa;IAqBrB;;OAEG;IACH,oBAAoB,CAAC,GAAG,EAAE,OAAO,GAAG,cAAc;IAYlD;;OAEG;IACG,YAAY,CAChB,IAAI,EAAE,OAAO,EACb,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,OAAO,CAAC;IAOnB;;OAEG;IACG,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAM9C;;OAEG;IACG,YAAY,CAChB,QAAQ,EAAE,gBAAgB,EAC1B,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,QAAQ,GACZ,OAAO,CAAC,IAAI,CAAC;IA2ChB;;;OAGG;IACG,aAAa,CACjB,UAAU,EAAE,MAAM,EAClB,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,QAAQ,GACZ,OAAO,CAAC,OAAO,CAAC;CASpB;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAC1B,aAAa,EAAE,MAAM,GAAG,SAAS,EACjC,WAAW,CAAC,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GACtC,MAAM,CAQR;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,YAAY,EAAE,YAAY,EAC1B,UAAU,CAAC,EAAE,MAAM,EACnB,WAAW,CAAC,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GACtC,gBAAgB,CAElB"}
|
|
@@ -1,17 +1,36 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.ResponseResolver = void 0;
|
|
7
|
+
exports.resolveDelay = resolveDelay;
|
|
4
8
|
exports.createResponseResolver = createResponseResolver;
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
5
10
|
const types_js_1 = require("./types.js");
|
|
6
11
|
/**
|
|
7
12
|
* Resolves which response to send for incoming requests
|
|
8
13
|
* Uses StateManager to determine active scenario based on priority chain
|
|
9
14
|
*/
|
|
10
15
|
class ResponseResolver {
|
|
11
|
-
constructor(stateManager) {
|
|
16
|
+
constructor(stateManager, configPath = '', globalDelay) {
|
|
12
17
|
this.endpoints = new Map();
|
|
13
18
|
this.routeMap = new Map();
|
|
14
19
|
this.stateManager = stateManager;
|
|
20
|
+
this.configPath = configPath;
|
|
21
|
+
this.globalDelay = globalDelay;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Update the config path (used when config is reloaded from a new location)
|
|
25
|
+
*/
|
|
26
|
+
setConfigPath(configPath) {
|
|
27
|
+
this.configPath = configPath;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Update the global delay at runtime
|
|
31
|
+
*/
|
|
32
|
+
setGlobalDelay(globalDelay) {
|
|
33
|
+
this.globalDelay = globalDelay;
|
|
15
34
|
}
|
|
16
35
|
/**
|
|
17
36
|
* Register endpoints for routing
|
|
@@ -80,14 +99,18 @@ class ResponseResolver {
|
|
|
80
99
|
* Build the resolved response from endpoint and scenario
|
|
81
100
|
*/
|
|
82
101
|
buildResponse(endpoint, scenario, req) {
|
|
83
|
-
|
|
102
|
+
const resolved = {
|
|
84
103
|
status: scenario.status,
|
|
85
104
|
headers: scenario.headers ?? {},
|
|
86
105
|
body: scenario.body,
|
|
87
|
-
delay: scenario.delay
|
|
106
|
+
delay: resolveDelay(scenario.delay, this.globalDelay),
|
|
88
107
|
scenario,
|
|
89
108
|
endpoint,
|
|
90
109
|
};
|
|
110
|
+
if (scenario.file) {
|
|
111
|
+
resolved.filePath = path_1.default.resolve(this.configPath, scenario.file);
|
|
112
|
+
}
|
|
113
|
+
return resolved;
|
|
91
114
|
}
|
|
92
115
|
/**
|
|
93
116
|
* Create request context for dynamic body functions
|
|
@@ -130,6 +153,24 @@ class ResponseResolver {
|
|
|
130
153
|
for (const [key, value] of Object.entries(resolved.headers)) {
|
|
131
154
|
res.setHeader(key, value);
|
|
132
155
|
}
|
|
156
|
+
// File download response
|
|
157
|
+
if ((0, types_js_1.isFileResponse)(resolved)) {
|
|
158
|
+
await new Promise((resolve, reject) => {
|
|
159
|
+
res.status(resolved.status).sendFile(resolved.filePath, (err) => {
|
|
160
|
+
if (err) {
|
|
161
|
+
// File not found or send error
|
|
162
|
+
if (!res.headersSent) {
|
|
163
|
+
res.status(404).json({ error: `File not found: ${resolved.filePath}` });
|
|
164
|
+
}
|
|
165
|
+
resolve();
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
resolve();
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
133
174
|
// Evaluate body (handle dynamic functions)
|
|
134
175
|
const context = this.createRequestContext(req);
|
|
135
176
|
const body = await this.evaluateBody(resolved.body, context);
|
|
@@ -159,9 +200,25 @@ class ResponseResolver {
|
|
|
159
200
|
}
|
|
160
201
|
}
|
|
161
202
|
exports.ResponseResolver = ResponseResolver;
|
|
203
|
+
/**
|
|
204
|
+
* Resolve the effective delay for a response.
|
|
205
|
+
* The scenario's own delay (when defined) always wins over the global fallback.
|
|
206
|
+
* A globalDelay tuple [min, max] produces a uniformly distributed random value.
|
|
207
|
+
*/
|
|
208
|
+
function resolveDelay(scenarioDelay, globalDelay) {
|
|
209
|
+
if (scenarioDelay !== undefined)
|
|
210
|
+
return scenarioDelay;
|
|
211
|
+
if (globalDelay === undefined)
|
|
212
|
+
return 0;
|
|
213
|
+
if (Array.isArray(globalDelay)) {
|
|
214
|
+
const [min, max] = globalDelay;
|
|
215
|
+
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
216
|
+
}
|
|
217
|
+
return globalDelay;
|
|
218
|
+
}
|
|
162
219
|
/**
|
|
163
220
|
* Create a new ResponseResolver instance
|
|
164
221
|
*/
|
|
165
|
-
function createResponseResolver(stateManager) {
|
|
166
|
-
return new ResponseResolver(stateManager);
|
|
222
|
+
function createResponseResolver(stateManager, configPath, globalDelay) {
|
|
223
|
+
return new ResponseResolver(stateManager, configPath, globalDelay);
|
|
167
224
|
}
|
package/dist/resolver/types.d.ts
CHANGED
|
@@ -10,6 +10,8 @@ export interface ResolvedResponse {
|
|
|
10
10
|
headers: Record<string, string>;
|
|
11
11
|
/** Response body */
|
|
12
12
|
body: unknown;
|
|
13
|
+
/** Absolute path to file for file download responses */
|
|
14
|
+
filePath?: string;
|
|
13
15
|
/** Delay in milliseconds before sending response */
|
|
14
16
|
delay: number;
|
|
15
17
|
/** The scenario that was resolved */
|
|
@@ -17,6 +19,12 @@ export interface ResolvedResponse {
|
|
|
17
19
|
/** The endpoint that was matched */
|
|
18
20
|
endpoint: EndpointConfig;
|
|
19
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* Check if a resolved response is a file download
|
|
24
|
+
*/
|
|
25
|
+
export declare function isFileResponse(resolved: ResolvedResponse): resolved is ResolvedResponse & {
|
|
26
|
+
filePath: string;
|
|
27
|
+
};
|
|
20
28
|
/**
|
|
21
29
|
* Context passed to dynamic body functions
|
|
22
30
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/resolver/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAY,MAAM,SAAS,CAAC;AACjD,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1E;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,uBAAuB;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,uBAAuB;IACvB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,oBAAoB;IACpB,IAAI,EAAE,OAAO,CAAC;IACd,oDAAoD;IACpD,KAAK,EAAE,MAAM,CAAC;IACd,qCAAqC;IACrC,QAAQ,EAAE,cAAc,CAAC;IACzB,oCAAoC;IACpC,QAAQ,EAAE,cAAc,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,6BAA6B;IAC7B,GAAG,EAAE,OAAO,CAAC;IACb,iCAAiC;IACjC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,8BAA8B;IAC9B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,mBAAmB;IACnB,IAAI,EAAE,OAAO,CAAC;IACd,sBAAsB;IACtB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC;IACvD,kBAAkB;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,mBAAmB;IACnB,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,EAAE,cAAc,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpF;;GAEG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,aAAa,CAElE"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/resolver/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAY,MAAM,SAAS,CAAC;AACjD,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1E;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,uBAAuB;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,uBAAuB;IACvB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,oBAAoB;IACpB,IAAI,EAAE,OAAO,CAAC;IACd,wDAAwD;IACxD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oDAAoD;IACpD,KAAK,EAAE,MAAM,CAAC;IACd,qCAAqC;IACrC,QAAQ,EAAE,cAAc,CAAC;IACzB,oCAAoC;IACpC,QAAQ,EAAE,cAAc,CAAC;CAC1B;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,gBAAgB,GAAG,QAAQ,IAAI,gBAAgB,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAE9G;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,6BAA6B;IAC7B,GAAG,EAAE,OAAO,CAAC;IACb,iCAAiC;IACjC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,8BAA8B;IAC9B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,mBAAmB;IACnB,IAAI,EAAE,OAAO,CAAC;IACd,sBAAsB;IACtB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC;IACvD,kBAAkB;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,mBAAmB;IACnB,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,EAAE,cAAc,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpF;;GAEG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,aAAa,CAElE"}
|
package/dist/resolver/types.js
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isFileResponse = isFileResponse;
|
|
3
4
|
exports.isDynamicBody = isDynamicBody;
|
|
5
|
+
/**
|
|
6
|
+
* Check if a resolved response is a file download
|
|
7
|
+
*/
|
|
8
|
+
function isFileResponse(resolved) {
|
|
9
|
+
return resolved.filePath !== undefined;
|
|
10
|
+
}
|
|
4
11
|
/**
|
|
5
12
|
* Check if a value is a dynamic body function
|
|
6
13
|
*/
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const vitest_1 = require("vitest");
|
|
4
4
|
const endpoint_schema_js_1 = require("../endpoint.schema.js");
|
|
5
|
+
const scenario_schema_js_1 = require("../scenario.schema.js");
|
|
5
6
|
(0, vitest_1.describe)('EndpointConfigSchema', () => {
|
|
6
7
|
const validEndpoint = {
|
|
7
8
|
id: 'get-user-data',
|
|
@@ -101,3 +102,50 @@ const endpoint_schema_js_1 = require("../endpoint.schema.js");
|
|
|
101
102
|
(0, vitest_1.expect)(result.duplicates).toContain('users');
|
|
102
103
|
});
|
|
103
104
|
});
|
|
105
|
+
(0, vitest_1.describe)('ScenarioConfigSchema - file field', () => {
|
|
106
|
+
const baseScenario = {
|
|
107
|
+
id: 'download',
|
|
108
|
+
name: 'Download',
|
|
109
|
+
status: 200,
|
|
110
|
+
};
|
|
111
|
+
(0, vitest_1.it)('should accept a scenario with only a file field', () => {
|
|
112
|
+
const result = scenario_schema_js_1.ScenarioConfigSchema.safeParse({
|
|
113
|
+
...baseScenario,
|
|
114
|
+
file: './assets/report.pdf',
|
|
115
|
+
});
|
|
116
|
+
(0, vitest_1.expect)(result.success).toBe(true);
|
|
117
|
+
if (result.success) {
|
|
118
|
+
(0, vitest_1.expect)(result.data.file).toBe('./assets/report.pdf');
|
|
119
|
+
(0, vitest_1.expect)(result.data.body).toBeUndefined();
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
(0, vitest_1.it)('should accept a scenario with only a body field', () => {
|
|
123
|
+
const result = scenario_schema_js_1.ScenarioConfigSchema.safeParse({
|
|
124
|
+
...baseScenario,
|
|
125
|
+
body: { ok: true },
|
|
126
|
+
});
|
|
127
|
+
(0, vitest_1.expect)(result.success).toBe(true);
|
|
128
|
+
});
|
|
129
|
+
(0, vitest_1.it)('should accept a scenario with neither body nor file', () => {
|
|
130
|
+
const result = scenario_schema_js_1.ScenarioConfigSchema.safeParse(baseScenario);
|
|
131
|
+
(0, vitest_1.expect)(result.success).toBe(true);
|
|
132
|
+
});
|
|
133
|
+
(0, vitest_1.it)('should reject a scenario with both body and file', () => {
|
|
134
|
+
const result = scenario_schema_js_1.ScenarioConfigSchema.safeParse({
|
|
135
|
+
...baseScenario,
|
|
136
|
+
body: { data: 'some json' },
|
|
137
|
+
file: './assets/report.pdf',
|
|
138
|
+
});
|
|
139
|
+
(0, vitest_1.expect)(result.success).toBe(false);
|
|
140
|
+
if (!result.success) {
|
|
141
|
+
(0, vitest_1.expect)(result.error.issues[0].message).toContain("cannot specify both 'body' and 'file'");
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
(0, vitest_1.it)('should reject an empty file path', () => {
|
|
145
|
+
const result = scenario_schema_js_1.ScenarioConfigSchema.safeParse({
|
|
146
|
+
...baseScenario,
|
|
147
|
+
file: '',
|
|
148
|
+
});
|
|
149
|
+
(0, vitest_1.expect)(result.success).toBe(false);
|
|
150
|
+
});
|
|
151
|
+
});
|
|
@@ -11,26 +11,45 @@ export declare const EndpointConfigSchema: z.ZodEffects<z.ZodObject<{
|
|
|
11
11
|
id: z.ZodString;
|
|
12
12
|
path: z.ZodString;
|
|
13
13
|
method: z.ZodEnum<["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD"]>;
|
|
14
|
-
scenarios: z.ZodArray<z.ZodObject<{
|
|
14
|
+
scenarios: z.ZodArray<z.ZodEffects<z.ZodObject<{
|
|
15
15
|
id: z.ZodString;
|
|
16
16
|
name: z.ZodString;
|
|
17
17
|
status: z.ZodNumber;
|
|
18
18
|
headers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
19
|
-
body: z.ZodUnion<[z.ZodRecord<z.ZodString, z.ZodUnknown>, z.ZodArray<z.ZodUnknown, "many">, z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodNull, z.ZodFunction<z.ZodTuple<[z.ZodAny], z.ZodUnknown>, z.ZodAny>]
|
|
19
|
+
body: z.ZodOptional<z.ZodUnion<[z.ZodRecord<z.ZodString, z.ZodUnknown>, z.ZodArray<z.ZodUnknown, "many">, z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodNull, z.ZodFunction<z.ZodTuple<[z.ZodAny], z.ZodUnknown>, z.ZodAny>]>>;
|
|
20
|
+
file: z.ZodOptional<z.ZodString>;
|
|
20
21
|
delay: z.ZodOptional<z.ZodNumber>;
|
|
21
22
|
}, "strip", z.ZodTypeAny, {
|
|
22
23
|
status: number;
|
|
23
24
|
id: string;
|
|
24
25
|
name: string;
|
|
25
|
-
body: string | number | boolean | unknown[] | Record<string, unknown> | ((args_0: any, ...args: unknown[]) => any) | null;
|
|
26
26
|
headers?: Record<string, string> | undefined;
|
|
27
|
+
body?: string | number | boolean | unknown[] | Record<string, unknown> | ((args_0: any, ...args: unknown[]) => any) | null | undefined;
|
|
28
|
+
file?: string | undefined;
|
|
27
29
|
delay?: number | undefined;
|
|
28
30
|
}, {
|
|
29
31
|
status: number;
|
|
30
32
|
id: string;
|
|
31
33
|
name: string;
|
|
32
|
-
body: string | number | boolean | unknown[] | Record<string, unknown> | ((args_0: any, ...args: unknown[]) => any) | null;
|
|
33
34
|
headers?: Record<string, string> | undefined;
|
|
35
|
+
body?: string | number | boolean | unknown[] | Record<string, unknown> | ((args_0: any, ...args: unknown[]) => any) | null | undefined;
|
|
36
|
+
file?: string | undefined;
|
|
37
|
+
delay?: number | undefined;
|
|
38
|
+
}>, {
|
|
39
|
+
status: number;
|
|
40
|
+
id: string;
|
|
41
|
+
name: string;
|
|
42
|
+
headers?: Record<string, string> | undefined;
|
|
43
|
+
body?: string | number | boolean | unknown[] | Record<string, unknown> | ((args_0: any, ...args: unknown[]) => any) | null | undefined;
|
|
44
|
+
file?: string | undefined;
|
|
45
|
+
delay?: number | undefined;
|
|
46
|
+
}, {
|
|
47
|
+
status: number;
|
|
48
|
+
id: string;
|
|
49
|
+
name: string;
|
|
50
|
+
headers?: Record<string, string> | undefined;
|
|
51
|
+
body?: string | number | boolean | unknown[] | Record<string, unknown> | ((args_0: any, ...args: unknown[]) => any) | null | undefined;
|
|
52
|
+
file?: string | undefined;
|
|
34
53
|
delay?: number | undefined;
|
|
35
54
|
}>, "many">;
|
|
36
55
|
defaultScenarioId: z.ZodString;
|
|
@@ -42,8 +61,9 @@ export declare const EndpointConfigSchema: z.ZodEffects<z.ZodObject<{
|
|
|
42
61
|
status: number;
|
|
43
62
|
id: string;
|
|
44
63
|
name: string;
|
|
45
|
-
body: string | number | boolean | unknown[] | Record<string, unknown> | ((args_0: any, ...args: unknown[]) => any) | null;
|
|
46
64
|
headers?: Record<string, string> | undefined;
|
|
65
|
+
body?: string | number | boolean | unknown[] | Record<string, unknown> | ((args_0: any, ...args: unknown[]) => any) | null | undefined;
|
|
66
|
+
file?: string | undefined;
|
|
47
67
|
delay?: number | undefined;
|
|
48
68
|
}[];
|
|
49
69
|
defaultScenarioId: string;
|
|
@@ -55,8 +75,9 @@ export declare const EndpointConfigSchema: z.ZodEffects<z.ZodObject<{
|
|
|
55
75
|
status: number;
|
|
56
76
|
id: string;
|
|
57
77
|
name: string;
|
|
58
|
-
body: string | number | boolean | unknown[] | Record<string, unknown> | ((args_0: any, ...args: unknown[]) => any) | null;
|
|
59
78
|
headers?: Record<string, string> | undefined;
|
|
79
|
+
body?: string | number | boolean | unknown[] | Record<string, unknown> | ((args_0: any, ...args: unknown[]) => any) | null | undefined;
|
|
80
|
+
file?: string | undefined;
|
|
60
81
|
delay?: number | undefined;
|
|
61
82
|
}[];
|
|
62
83
|
defaultScenarioId: string;
|
|
@@ -68,8 +89,9 @@ export declare const EndpointConfigSchema: z.ZodEffects<z.ZodObject<{
|
|
|
68
89
|
status: number;
|
|
69
90
|
id: string;
|
|
70
91
|
name: string;
|
|
71
|
-
body: string | number | boolean | unknown[] | Record<string, unknown> | ((args_0: any, ...args: unknown[]) => any) | null;
|
|
72
92
|
headers?: Record<string, string> | undefined;
|
|
93
|
+
body?: string | number | boolean | unknown[] | Record<string, unknown> | ((args_0: any, ...args: unknown[]) => any) | null | undefined;
|
|
94
|
+
file?: string | undefined;
|
|
73
95
|
delay?: number | undefined;
|
|
74
96
|
}[];
|
|
75
97
|
defaultScenarioId: string;
|
|
@@ -81,8 +103,9 @@ export declare const EndpointConfigSchema: z.ZodEffects<z.ZodObject<{
|
|
|
81
103
|
status: number;
|
|
82
104
|
id: string;
|
|
83
105
|
name: string;
|
|
84
|
-
body: string | number | boolean | unknown[] | Record<string, unknown> | ((args_0: any, ...args: unknown[]) => any) | null;
|
|
85
106
|
headers?: Record<string, string> | undefined;
|
|
107
|
+
body?: string | number | boolean | unknown[] | Record<string, unknown> | ((args_0: any, ...args: unknown[]) => any) | null | undefined;
|
|
108
|
+
file?: string | undefined;
|
|
86
109
|
delay?: number | undefined;
|
|
87
110
|
}[];
|
|
88
111
|
defaultScenarioId: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"endpoint.schema.d.ts","sourceRoot":"","sources":["../../src/schemas/endpoint.schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAQxB;;GAEG;AACH,eAAO,MAAM,UAAU,aAKuC,CAAC;AAE/D;;;GAGG;AACH,eAAO,MAAM,oBAAoB
|
|
1
|
+
{"version":3,"file":"endpoint.schema.d.ts","sourceRoot":"","sources":["../../src/schemas/endpoint.schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAQxB;;GAEG;AACH,eAAO,MAAM,UAAU,aAKuC,CAAC;AAE/D;;;GAGG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA8B7B,CAAC;AAEL,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,cAAc,EAAE,GAC1B;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE,CAgBxE;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CACvC,SAAS,EAAE,cAAc,EAAE,GAC1B;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,UAAU,EAAE,MAAM,EAAE,CAAA;CAAE,CAgB1C"}
|
|
@@ -17,30 +17,54 @@ export type ResponseBody = z.infer<typeof ResponseBodySchema>;
|
|
|
17
17
|
* Response delay in milliseconds (0-30000ms max to prevent hanging)
|
|
18
18
|
*/
|
|
19
19
|
export declare const DelaySchema: z.ZodOptional<z.ZodNumber>;
|
|
20
|
+
/**
|
|
21
|
+
* File path for file download responses
|
|
22
|
+
* Relative to the config directory
|
|
23
|
+
*/
|
|
24
|
+
export declare const FilePathSchema: z.ZodString;
|
|
20
25
|
/**
|
|
21
26
|
* Scenario configuration schema
|
|
22
27
|
* Defines a single response scenario for an endpoint
|
|
23
28
|
*/
|
|
24
|
-
export declare const ScenarioConfigSchema: z.ZodObject<{
|
|
29
|
+
export declare const ScenarioConfigSchema: z.ZodEffects<z.ZodObject<{
|
|
25
30
|
id: z.ZodString;
|
|
26
31
|
name: z.ZodString;
|
|
27
32
|
status: z.ZodNumber;
|
|
28
33
|
headers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
29
|
-
body: z.ZodUnion<[z.ZodRecord<z.ZodString, z.ZodUnknown>, z.ZodArray<z.ZodUnknown, "many">, z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodNull, z.ZodFunction<z.ZodTuple<[z.ZodAny], z.ZodUnknown>, z.ZodAny>]
|
|
34
|
+
body: z.ZodOptional<z.ZodUnion<[z.ZodRecord<z.ZodString, z.ZodUnknown>, z.ZodArray<z.ZodUnknown, "many">, z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodNull, z.ZodFunction<z.ZodTuple<[z.ZodAny], z.ZodUnknown>, z.ZodAny>]>>;
|
|
35
|
+
file: z.ZodOptional<z.ZodString>;
|
|
30
36
|
delay: z.ZodOptional<z.ZodNumber>;
|
|
31
37
|
}, "strip", z.ZodTypeAny, {
|
|
32
38
|
status: number;
|
|
33
39
|
id: string;
|
|
34
40
|
name: string;
|
|
35
|
-
body: string | number | boolean | unknown[] | Record<string, unknown> | ((args_0: any, ...args: unknown[]) => any) | null;
|
|
36
41
|
headers?: Record<string, string> | undefined;
|
|
42
|
+
body?: string | number | boolean | unknown[] | Record<string, unknown> | ((args_0: any, ...args: unknown[]) => any) | null | undefined;
|
|
43
|
+
file?: string | undefined;
|
|
44
|
+
delay?: number | undefined;
|
|
45
|
+
}, {
|
|
46
|
+
status: number;
|
|
47
|
+
id: string;
|
|
48
|
+
name: string;
|
|
49
|
+
headers?: Record<string, string> | undefined;
|
|
50
|
+
body?: string | number | boolean | unknown[] | Record<string, unknown> | ((args_0: any, ...args: unknown[]) => any) | null | undefined;
|
|
51
|
+
file?: string | undefined;
|
|
52
|
+
delay?: number | undefined;
|
|
53
|
+
}>, {
|
|
54
|
+
status: number;
|
|
55
|
+
id: string;
|
|
56
|
+
name: string;
|
|
57
|
+
headers?: Record<string, string> | undefined;
|
|
58
|
+
body?: string | number | boolean | unknown[] | Record<string, unknown> | ((args_0: any, ...args: unknown[]) => any) | null | undefined;
|
|
59
|
+
file?: string | undefined;
|
|
37
60
|
delay?: number | undefined;
|
|
38
61
|
}, {
|
|
39
62
|
status: number;
|
|
40
63
|
id: string;
|
|
41
64
|
name: string;
|
|
42
|
-
body: string | number | boolean | unknown[] | Record<string, unknown> | ((args_0: any, ...args: unknown[]) => any) | null;
|
|
43
65
|
headers?: Record<string, string> | undefined;
|
|
66
|
+
body?: string | number | boolean | unknown[] | Record<string, unknown> | ((args_0: any, ...args: unknown[]) => any) | null | undefined;
|
|
67
|
+
file?: string | undefined;
|
|
44
68
|
delay?: number | undefined;
|
|
45
69
|
}>;
|
|
46
70
|
export type ScenarioConfig = z.infer<typeof ScenarioConfigSchema>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scenario.schema.d.ts","sourceRoot":"","sources":["../../src/schemas/scenario.schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB;;GAEG;AACH,eAAO,MAAM,QAAQ,aAOlB,CAAC;AAEJ;;GAEG;AACH,eAAO,MAAM,UAAU,aAIW,CAAC;AAEnC;;;GAGG;AACH,eAAO,MAAM,kBAAkB,0MAQ7B,CAAC;AAEH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE9D;;GAEG;AACH,eAAO,MAAM,WAAW,4BAMqB,CAAC;AAE9C;;;GAGG;AACH,eAAO,MAAM,oBAAoB
|
|
1
|
+
{"version":3,"file":"scenario.schema.d.ts","sourceRoot":"","sources":["../../src/schemas/scenario.schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB;;GAEG;AACH,eAAO,MAAM,QAAQ,aAOlB,CAAC;AAEJ;;GAEG;AACH,eAAO,MAAM,UAAU,aAIW,CAAC;AAEnC;;;GAGG;AACH,eAAO,MAAM,kBAAkB,0MAQ7B,CAAC;AAEH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE9D;;GAEG;AACH,eAAO,MAAM,WAAW,4BAMqB,CAAC;AAE9C;;;GAGG;AACH,eAAO,MAAM,cAAc,aAI4D,CAAC;AAExF;;;GAGG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAkB7B,CAAC;AAEL,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE;;GAEG;AACH,wBAAgB,yBAAyB,CACvC,SAAS,EAAE,cAAc,EAAE,GAC1B;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,UAAU,EAAE,MAAM,EAAE,CAAA;CAAE,CAgB1C"}
|