@schmock/core 2.1.1 → 2.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +696 -1
- package/package.json +1 -1
- package/dist/schmock.d.ts +0 -692
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,699 @@
|
|
|
1
|
-
|
|
1
|
+
// >>> schmock ambient namespace <<<
|
|
2
|
+
declare global {
|
|
3
|
+
/**
|
|
4
|
+
* Schmock - Schema-driven mock API generator with callable API
|
|
5
|
+
* @packageDocumentation
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
namespace Schmock {
|
|
9
|
+
type JSONSchema7 = import("json-schema").JSONSchema7;
|
|
10
|
+
/**
|
|
11
|
+
* HTTP methods supported by Schmock
|
|
12
|
+
*/
|
|
13
|
+
type HttpMethod =
|
|
14
|
+
| "GET"
|
|
15
|
+
| "POST"
|
|
16
|
+
| "PUT"
|
|
17
|
+
| "DELETE"
|
|
18
|
+
| "PATCH"
|
|
19
|
+
| "HEAD"
|
|
20
|
+
| "OPTIONS";
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Route key format: 'METHOD /path'
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* 'GET /users'
|
|
27
|
+
* 'POST /users/:id'
|
|
28
|
+
* 'DELETE /api/posts/:postId/comments/:commentId'
|
|
29
|
+
*/
|
|
30
|
+
type RouteKey = `${HttpMethod} ${string}`;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Plugin interface for extending Schmock functionality
|
|
34
|
+
*/
|
|
35
|
+
interface Plugin {
|
|
36
|
+
/** Unique plugin identifier */
|
|
37
|
+
name: string;
|
|
38
|
+
/** Plugin version (semver) */
|
|
39
|
+
version?: string;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Called once when the plugin is added via .pipe()
|
|
43
|
+
* Use this to register routes or configure the instance at setup time
|
|
44
|
+
* @param instance - The callable mock instance
|
|
45
|
+
*/
|
|
46
|
+
install?(instance: CallableMockInstance): void;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Process the request through this plugin
|
|
50
|
+
* First plugin to set response becomes the generator, others transform
|
|
51
|
+
* @param context - Plugin context with request details
|
|
52
|
+
* @param response - Response from previous plugin (if any)
|
|
53
|
+
* @returns Updated context and response
|
|
54
|
+
*/
|
|
55
|
+
process(context: PluginContext, response?: unknown): PluginResult | Promise<PluginResult>;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Called when an error occurs
|
|
59
|
+
* Can handle, transform, or suppress errors
|
|
60
|
+
* @param error - The error that occurred
|
|
61
|
+
* @param context - Plugin context
|
|
62
|
+
* @returns Modified error, response data, or void to continue error propagation
|
|
63
|
+
*/
|
|
64
|
+
onError?(error: Error, context: PluginContext): Error | ResponseResult | void | Promise<Error | ResponseResult | void>;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Alias for response body type
|
|
69
|
+
*/
|
|
70
|
+
type ResponseBody = unknown;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Result returned by plugin process method
|
|
74
|
+
*/
|
|
75
|
+
interface PluginResult {
|
|
76
|
+
/** Updated context */
|
|
77
|
+
context: PluginContext;
|
|
78
|
+
/** Response data (if generated/modified) */
|
|
79
|
+
response?: unknown;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Context passed through plugin pipeline
|
|
84
|
+
*/
|
|
85
|
+
interface PluginContext {
|
|
86
|
+
/** Request path */
|
|
87
|
+
path: string;
|
|
88
|
+
/** Matched route configuration */
|
|
89
|
+
route: RouteConfig;
|
|
90
|
+
/** HTTP method */
|
|
91
|
+
method: HttpMethod;
|
|
92
|
+
/** Route parameters */
|
|
93
|
+
params: Record<string, string>;
|
|
94
|
+
/** Query parameters */
|
|
95
|
+
query: Record<string, string>;
|
|
96
|
+
/** Request headers */
|
|
97
|
+
headers: Record<string, string>;
|
|
98
|
+
/** Request body */
|
|
99
|
+
body?: unknown;
|
|
100
|
+
/** Shared state between plugins for this request */
|
|
101
|
+
state: Map<string, unknown>;
|
|
102
|
+
/** Route-specific state */
|
|
103
|
+
routeState?: Record<string, unknown>;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// ===== Callable API Types =====
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Global configuration options for the mock instance
|
|
110
|
+
*/
|
|
111
|
+
interface GlobalConfig {
|
|
112
|
+
/** Base path prefix for all routes */
|
|
113
|
+
namespace?: string;
|
|
114
|
+
/** Response delay in ms, or [min, max] for random delay */
|
|
115
|
+
delay?: number | [number, number];
|
|
116
|
+
/** Enable debug mode for detailed logging */
|
|
117
|
+
debug?: boolean;
|
|
118
|
+
/** Initial shared state object */
|
|
119
|
+
state?: Record<string, unknown>;
|
|
120
|
+
/**
|
|
121
|
+
* Maximum number of requests retained in history (FIFO eviction).
|
|
122
|
+
* Defaults to unbounded; set this to cap memory growth in long-running servers.
|
|
123
|
+
*/
|
|
124
|
+
maxHistorySize?: number;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Route-specific configuration options
|
|
129
|
+
*/
|
|
130
|
+
interface RouteConfig {
|
|
131
|
+
/** MIME type for content type validation (auto-detected if not provided) */
|
|
132
|
+
contentType?: string;
|
|
133
|
+
/** Per-route response delay in ms, or [min, max] for random delay (overrides global) */
|
|
134
|
+
delay?: number | [number, number];
|
|
135
|
+
/** Additional route-specific options */
|
|
136
|
+
[key: string]: unknown;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Generator types that can be passed to route definitions
|
|
141
|
+
*/
|
|
142
|
+
type Generator =
|
|
143
|
+
| GeneratorFunction
|
|
144
|
+
| StaticData
|
|
145
|
+
| JSONSchema7;
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Function that generates responses
|
|
149
|
+
*/
|
|
150
|
+
type GeneratorFunction = (context: RequestContext) => ResponseResult | Promise<ResponseResult>;
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Static data (non-function) that gets returned as-is
|
|
154
|
+
*/
|
|
155
|
+
type StaticData = string | number | boolean | null | undefined | Record<string, unknown> | unknown[];
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Context passed to generator functions
|
|
159
|
+
*/
|
|
160
|
+
interface RequestContext {
|
|
161
|
+
/** HTTP method */
|
|
162
|
+
method: HttpMethod;
|
|
163
|
+
/** Request path */
|
|
164
|
+
path: string;
|
|
165
|
+
/** Route parameters (e.g., :id) */
|
|
166
|
+
params: Record<string, string>;
|
|
167
|
+
/** Query string parameters */
|
|
168
|
+
query: Record<string, string>;
|
|
169
|
+
/** Request headers */
|
|
170
|
+
headers: Record<string, string>;
|
|
171
|
+
/** Request body (for POST, PUT, PATCH) */
|
|
172
|
+
body?: unknown;
|
|
173
|
+
/** Shared mutable state */
|
|
174
|
+
state: Record<string, unknown>;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Response result types:
|
|
179
|
+
* - Any value: returns as 200 OK
|
|
180
|
+
* - [status, body]: custom status with body
|
|
181
|
+
* - [status, body, headers]: custom status, body, and headers
|
|
182
|
+
*/
|
|
183
|
+
type ResponseResult =
|
|
184
|
+
| ResponseBody
|
|
185
|
+
| [number, unknown]
|
|
186
|
+
| [number, unknown, Record<string, string>];
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Response object returned by handle method
|
|
190
|
+
*/
|
|
191
|
+
interface Response {
|
|
192
|
+
status: number;
|
|
193
|
+
body: unknown;
|
|
194
|
+
headers: Record<string, string>;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Options for handle method
|
|
199
|
+
*/
|
|
200
|
+
interface RequestOptions {
|
|
201
|
+
headers?: Record<string, string>;
|
|
202
|
+
body?: unknown;
|
|
203
|
+
query?: Record<string, string>;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Record of a single request handled by the mock
|
|
208
|
+
*/
|
|
209
|
+
interface RequestRecord {
|
|
210
|
+
/** HTTP method */
|
|
211
|
+
method: HttpMethod;
|
|
212
|
+
/** Request path (without namespace) */
|
|
213
|
+
path: string;
|
|
214
|
+
/** Extracted route parameters */
|
|
215
|
+
params: Record<string, string>;
|
|
216
|
+
/** Query parameters */
|
|
217
|
+
query: Record<string, string>;
|
|
218
|
+
/** Request headers */
|
|
219
|
+
headers: Record<string, string>;
|
|
220
|
+
/** Request body */
|
|
221
|
+
body: unknown;
|
|
222
|
+
/** Unix timestamp (ms) when request was handled */
|
|
223
|
+
timestamp: number;
|
|
224
|
+
/** Response returned for this request */
|
|
225
|
+
response: { status: number; body: unknown };
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Main callable mock instance interface
|
|
230
|
+
*/
|
|
231
|
+
interface CallableMockInstance {
|
|
232
|
+
/**
|
|
233
|
+
* Define a route by calling the instance directly
|
|
234
|
+
*
|
|
235
|
+
* @param route - Route pattern in format 'METHOD /path'
|
|
236
|
+
* @param generator - Response generator (function, static data, or schema)
|
|
237
|
+
* @param config - Route-specific configuration
|
|
238
|
+
* @returns The same instance for method chaining
|
|
239
|
+
*
|
|
240
|
+
* @example
|
|
241
|
+
* ```typescript
|
|
242
|
+
* const mock = schmock()
|
|
243
|
+
* mock('GET /users', () => [...users], { contentType: 'application/json' })
|
|
244
|
+
* mock('POST /users', userData, { contentType: 'application/json' })
|
|
245
|
+
* ```
|
|
246
|
+
*/
|
|
247
|
+
(route: RouteKey, generator: Generator, config?: RouteConfig): CallableMockInstance;
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Add a plugin to the pipeline
|
|
251
|
+
*
|
|
252
|
+
* @param plugin - Plugin to add to the pipeline
|
|
253
|
+
* @returns The same instance for method chaining
|
|
254
|
+
*
|
|
255
|
+
* @example
|
|
256
|
+
* ```typescript
|
|
257
|
+
* mock('GET /users', generator, config)
|
|
258
|
+
* .pipe(authPlugin())
|
|
259
|
+
* .pipe(corsPlugin())
|
|
260
|
+
* ```
|
|
261
|
+
*/
|
|
262
|
+
pipe(plugin: Plugin): CallableMockInstance;
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Handle a request and return a response
|
|
266
|
+
*
|
|
267
|
+
* @param method - HTTP method
|
|
268
|
+
* @param path - Request path
|
|
269
|
+
* @param options - Request options (headers, body, query)
|
|
270
|
+
* @returns Promise resolving to response object
|
|
271
|
+
*
|
|
272
|
+
* @example
|
|
273
|
+
* ```typescript
|
|
274
|
+
* const response = await mock.handle('GET', '/users', {
|
|
275
|
+
* headers: { 'Authorization': 'Bearer token' }
|
|
276
|
+
* })
|
|
277
|
+
* ```
|
|
278
|
+
*/
|
|
279
|
+
handle(method: HttpMethod, path: string, options?: RequestOptions): Promise<Response>;
|
|
280
|
+
|
|
281
|
+
// ===== Request Spy / History API =====
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Get all recorded requests, optionally filtered by method and path
|
|
285
|
+
*
|
|
286
|
+
* @param method - Filter by HTTP method
|
|
287
|
+
* @param path - Filter by request path
|
|
288
|
+
* @returns Array of request records
|
|
289
|
+
*/
|
|
290
|
+
history(): RequestRecord[];
|
|
291
|
+
history(method: HttpMethod, path: string): RequestRecord[];
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Check if any request was made, optionally for a specific route
|
|
295
|
+
*
|
|
296
|
+
* @param method - Filter by HTTP method
|
|
297
|
+
* @param path - Filter by request path
|
|
298
|
+
* @returns true if at least one matching request was recorded
|
|
299
|
+
*/
|
|
300
|
+
called(): boolean;
|
|
301
|
+
called(method: HttpMethod, path: string): boolean;
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Get the number of recorded requests, optionally for a specific route
|
|
305
|
+
*
|
|
306
|
+
* @param method - Filter by HTTP method
|
|
307
|
+
* @param path - Filter by request path
|
|
308
|
+
* @returns Number of matching requests
|
|
309
|
+
*/
|
|
310
|
+
callCount(): number;
|
|
311
|
+
callCount(method: HttpMethod, path: string): number;
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Get the most recent request, optionally for a specific route
|
|
315
|
+
*
|
|
316
|
+
* @param method - Filter by HTTP method
|
|
317
|
+
* @param path - Filter by request path
|
|
318
|
+
* @returns Most recent matching request record, or undefined
|
|
319
|
+
*/
|
|
320
|
+
lastRequest(): RequestRecord | undefined;
|
|
321
|
+
lastRequest(method: HttpMethod, path: string): RequestRecord | undefined;
|
|
322
|
+
|
|
323
|
+
// ===== Reset / Lifecycle =====
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Clear all routes, state, plugins, and history
|
|
327
|
+
*/
|
|
328
|
+
reset(): void;
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Clear only request history, keep routes and state
|
|
332
|
+
*/
|
|
333
|
+
resetHistory(): void;
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Clear only state, keep routes and history
|
|
337
|
+
*/
|
|
338
|
+
resetState(): void;
|
|
339
|
+
|
|
340
|
+
// ===== Lifecycle Events =====
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Register an event listener
|
|
344
|
+
*/
|
|
345
|
+
on<E extends SchmockEvent>(event: E, listener: (data: SchmockEventMap[E]) => void): CallableMockInstance;
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Remove an event listener
|
|
349
|
+
*/
|
|
350
|
+
off<E extends SchmockEvent>(event: E, listener: (data: SchmockEventMap[E]) => void): CallableMockInstance;
|
|
351
|
+
|
|
352
|
+
// ===== Introspection =====
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Get all registered routes as an array of route info objects
|
|
356
|
+
*/
|
|
357
|
+
getRoutes(): RouteInfo[];
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Get the current shared state object
|
|
361
|
+
*/
|
|
362
|
+
getState(): Record<string, unknown>;
|
|
363
|
+
|
|
364
|
+
// ===== Standalone Server =====
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Start a standalone HTTP server
|
|
368
|
+
*
|
|
369
|
+
* @param port - Port to listen on (0 for random)
|
|
370
|
+
* @param hostname - Hostname to bind to (default: "127.0.0.1")
|
|
371
|
+
* @returns Promise resolving to server info with actual port and hostname
|
|
372
|
+
* @throws If the server is already running
|
|
373
|
+
*/
|
|
374
|
+
listen(port?: number, hostname?: string): Promise<ServerInfo>;
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Stop the standalone server (idempotent, no-op if not running)
|
|
378
|
+
*/
|
|
379
|
+
close(): void;
|
|
380
|
+
|
|
381
|
+
// ===== Fetch Interceptor =====
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Intercept globalThis.fetch and route requests through this mock.
|
|
385
|
+
* Client-side equivalent of listen().
|
|
386
|
+
*
|
|
387
|
+
* @param options - Intercept configuration
|
|
388
|
+
* @returns Handle with restore() to stop intercepting
|
|
389
|
+
* @throws If already intercepting (call restore() first)
|
|
390
|
+
*/
|
|
391
|
+
intercept(options?: InterceptOptions): InterceptHandle;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Information about a running standalone server
|
|
396
|
+
*/
|
|
397
|
+
interface ServerInfo {
|
|
398
|
+
/** Port the server is listening on */
|
|
399
|
+
port: number;
|
|
400
|
+
/** Hostname the server is bound to */
|
|
401
|
+
hostname: string;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// ===== Response Helpers =====
|
|
405
|
+
|
|
406
|
+
interface PaginateOptions {
|
|
407
|
+
page?: number;
|
|
408
|
+
pageSize?: number;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
interface PaginatedResponse<T> {
|
|
412
|
+
data: T[];
|
|
413
|
+
page: number;
|
|
414
|
+
pageSize: number;
|
|
415
|
+
total: number;
|
|
416
|
+
totalPages: number;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// ===== Adapter Types =====
|
|
420
|
+
|
|
421
|
+
interface AdapterRequest {
|
|
422
|
+
method: string;
|
|
423
|
+
path: string;
|
|
424
|
+
headers: Record<string, string>;
|
|
425
|
+
body?: unknown;
|
|
426
|
+
query: Record<string, string>;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
interface AdapterResponse {
|
|
430
|
+
status: number;
|
|
431
|
+
body: unknown;
|
|
432
|
+
headers: Record<string, string>;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
interface InterceptOptions {
|
|
436
|
+
/**
|
|
437
|
+
* Only intercept URLs matching this base.
|
|
438
|
+
*
|
|
439
|
+
* Two modes:
|
|
440
|
+
* - Path form ("/api"): match request pathnames whose prefix is the
|
|
441
|
+
* base path (with a segment-boundary check, so "/api" never
|
|
442
|
+
* matches "/apiv2").
|
|
443
|
+
* - Origin form ("https://api.example.com" or
|
|
444
|
+
* "https://api.example.com/v1"): require the request origin to
|
|
445
|
+
* match the base origin AND, if a base path is present, the
|
|
446
|
+
* request pathname to start with it. Relative-URL fetches
|
|
447
|
+
* (no origin) won't match an origin-form base.
|
|
448
|
+
*/
|
|
449
|
+
baseUrl?: string;
|
|
450
|
+
/** Pass unmatched routes to real fetch (default: true) */
|
|
451
|
+
passthrough?: boolean;
|
|
452
|
+
/** Modify request before Schmock handles it */
|
|
453
|
+
beforeRequest?: (
|
|
454
|
+
request: AdapterRequest,
|
|
455
|
+
) => AdapterRequest | void | Promise<AdapterRequest | void>;
|
|
456
|
+
/** Modify response before returning to caller */
|
|
457
|
+
beforeResponse?: (
|
|
458
|
+
response: AdapterResponse,
|
|
459
|
+
request: AdapterRequest,
|
|
460
|
+
) => AdapterResponse | void | Promise<AdapterResponse | void>;
|
|
461
|
+
/** Format errors into custom response bodies */
|
|
462
|
+
errorFormatter?: (error: Error) => unknown;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
interface InterceptHandle {
|
|
466
|
+
/** Stop intercepting and restore original fetch */
|
|
467
|
+
restore(): void;
|
|
468
|
+
/** Whether this interceptor is currently active */
|
|
469
|
+
readonly active: boolean;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// ===== Lifecycle Events =====
|
|
473
|
+
|
|
474
|
+
interface RequestStartEvent {
|
|
475
|
+
method: HttpMethod;
|
|
476
|
+
path: string;
|
|
477
|
+
headers: Record<string, string>;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
interface RequestMatchEvent {
|
|
481
|
+
method: HttpMethod;
|
|
482
|
+
path: string;
|
|
483
|
+
routePath: string;
|
|
484
|
+
params: Record<string, string>;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
interface RequestNotFoundEvent {
|
|
488
|
+
method: HttpMethod;
|
|
489
|
+
path: string;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
interface RequestEndEvent {
|
|
493
|
+
method: HttpMethod;
|
|
494
|
+
path: string;
|
|
495
|
+
status: number;
|
|
496
|
+
duration: number;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
type SchmockEventMap = {
|
|
500
|
+
"request:start": RequestStartEvent;
|
|
501
|
+
"request:match": RequestMatchEvent;
|
|
502
|
+
"request:notfound": RequestNotFoundEvent;
|
|
503
|
+
"request:end": RequestEndEvent;
|
|
504
|
+
};
|
|
505
|
+
|
|
506
|
+
type SchmockEvent = keyof SchmockEventMap;
|
|
507
|
+
|
|
508
|
+
// ===== Introspection Types =====
|
|
509
|
+
|
|
510
|
+
interface RouteInfo {
|
|
511
|
+
method: HttpMethod;
|
|
512
|
+
path: string;
|
|
513
|
+
hasParams: boolean;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// ===== OpenAPI Plugin Types =====
|
|
517
|
+
|
|
518
|
+
/**
|
|
519
|
+
* Per-resource configuration override for OpenAPI plugin
|
|
520
|
+
*/
|
|
521
|
+
interface ResourceOverride {
|
|
522
|
+
/** Override: which property in list response holds the items array (e.g. "data") */
|
|
523
|
+
listWrapProperty?: string;
|
|
524
|
+
/** Override: force flat array for list (ignores any wrapper in the spec) */
|
|
525
|
+
listFlat?: boolean;
|
|
526
|
+
/** Override: JSON Schema for error responses (404, etc.) */
|
|
527
|
+
errorSchema?: JSONSchema7;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/**
|
|
531
|
+
* Response header definition from an OpenAPI spec
|
|
532
|
+
*/
|
|
533
|
+
interface ResponseHeaderDef {
|
|
534
|
+
schema?: JSONSchema7;
|
|
535
|
+
description: string;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
/**
|
|
539
|
+
* Per-operation metadata auto-detected from spec or set via overrides
|
|
540
|
+
*/
|
|
541
|
+
interface CrudOperationMeta {
|
|
542
|
+
/** Full success response schema (wrapper + items) */
|
|
543
|
+
responseSchema?: JSONSchema7;
|
|
544
|
+
/** Response headers from spec */
|
|
545
|
+
responseHeaders?: Record<string, ResponseHeaderDef>;
|
|
546
|
+
/** Error response schemas keyed by status code */
|
|
547
|
+
errorSchemas?: Map<number, JSONSchema7>;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
// ===== Faker Plugin Types =====
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* Context for schema-based data generation
|
|
554
|
+
*/
|
|
555
|
+
interface SchemaGenerationContext {
|
|
556
|
+
schema: JSONSchema7;
|
|
557
|
+
count?: number;
|
|
558
|
+
overrides?: Record<string, unknown>;
|
|
559
|
+
params?: Record<string, string>;
|
|
560
|
+
state?: Record<string, unknown>;
|
|
561
|
+
query?: Record<string, string>;
|
|
562
|
+
seed?: number;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
/**
|
|
566
|
+
* Options for the faker plugin
|
|
567
|
+
*/
|
|
568
|
+
interface FakerPluginOptions {
|
|
569
|
+
schema: JSONSchema7;
|
|
570
|
+
count?: number;
|
|
571
|
+
overrides?: Record<string, unknown>;
|
|
572
|
+
seed?: number;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
// ===== Express Adapter Types =====
|
|
576
|
+
|
|
577
|
+
/**
|
|
578
|
+
* Override parts of a request before Schmock handles it
|
|
579
|
+
*/
|
|
580
|
+
interface AdapterRequestOverride {
|
|
581
|
+
method?: string;
|
|
582
|
+
path?: string;
|
|
583
|
+
headers?: Record<string, string>;
|
|
584
|
+
body?: unknown;
|
|
585
|
+
query?: Record<string, string>;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
/**
|
|
589
|
+
* Configuration options for Express adapter
|
|
590
|
+
*/
|
|
591
|
+
interface ExpressAdapterOptions {
|
|
592
|
+
errorFormatter?: (error: Error, req: unknown) => unknown;
|
|
593
|
+
passErrorsToNext?: boolean;
|
|
594
|
+
transformHeaders?: (
|
|
595
|
+
headers: Record<string, string | string[] | undefined>,
|
|
596
|
+
) => Record<string, string>;
|
|
597
|
+
transformQuery?: (query: Record<string, unknown>) => Record<string, string>;
|
|
598
|
+
beforeRequest?: (
|
|
599
|
+
req: unknown,
|
|
600
|
+
res: unknown,
|
|
601
|
+
) =>
|
|
602
|
+
| AdapterRequestOverride
|
|
603
|
+
| undefined
|
|
604
|
+
| Promise<AdapterRequestOverride | undefined>;
|
|
605
|
+
beforeResponse?: (
|
|
606
|
+
response: Response,
|
|
607
|
+
req: unknown,
|
|
608
|
+
res: unknown,
|
|
609
|
+
) => Response | undefined | Promise<Response | undefined>;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
// ===== Angular Adapter Types =====
|
|
613
|
+
|
|
614
|
+
/**
|
|
615
|
+
* Configuration options for Angular adapter
|
|
616
|
+
*/
|
|
617
|
+
interface AngularAdapterOptions {
|
|
618
|
+
baseUrl?: string;
|
|
619
|
+
passthrough?: boolean;
|
|
620
|
+
errorFormatter?: (error: Error, request: unknown) => unknown;
|
|
621
|
+
transformRequest?: (request: unknown) => AdapterRequestOverride;
|
|
622
|
+
transformResponse?: (
|
|
623
|
+
response: Response,
|
|
624
|
+
request: unknown,
|
|
625
|
+
) => Response;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
// ===== OpenAPI Plugin Options =====
|
|
629
|
+
|
|
630
|
+
/**
|
|
631
|
+
* Options for the OpenAPI plugin
|
|
632
|
+
*/
|
|
633
|
+
interface OpenApiOptions {
|
|
634
|
+
spec: string | object;
|
|
635
|
+
seed?: SeedConfig;
|
|
636
|
+
validateRequests?: boolean;
|
|
637
|
+
validateResponses?: boolean;
|
|
638
|
+
queryFeatures?: {
|
|
639
|
+
pagination?: boolean;
|
|
640
|
+
sorting?: boolean;
|
|
641
|
+
filtering?: boolean;
|
|
642
|
+
};
|
|
643
|
+
resources?: Record<string, ResourceOverride>;
|
|
644
|
+
debug?: boolean;
|
|
645
|
+
fakerSeed?: number;
|
|
646
|
+
security?: boolean;
|
|
647
|
+
/** Replace response schemas for specific routes. Key format: "METHOD /path" or "METHOD /path STATUS" */
|
|
648
|
+
schemas?: Record<string, import("json-schema").JSONSchema7>;
|
|
649
|
+
/** Called before generating a response body. Return a schema to replace the original, or void to keep it. */
|
|
650
|
+
onSchema?: (
|
|
651
|
+
schema: import("json-schema").JSONSchema7,
|
|
652
|
+
context: { method: string; path: string; params: Record<string, string>; query: Record<string, string>; headers: Record<string, string> },
|
|
653
|
+
) => import("json-schema").JSONSchema7 | undefined;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
/**
|
|
657
|
+
* Seed data source: inline array, file path, or auto-generate count
|
|
658
|
+
*/
|
|
659
|
+
type SeedSource = unknown[] | string | { count: number };
|
|
660
|
+
|
|
661
|
+
/**
|
|
662
|
+
* Seed configuration mapping resource names to seed sources
|
|
663
|
+
*/
|
|
664
|
+
type SeedConfig = Record<string, SeedSource>;
|
|
665
|
+
|
|
666
|
+
// ===== CLI Types =====
|
|
667
|
+
|
|
668
|
+
/**
|
|
669
|
+
* Options for the CLI server
|
|
670
|
+
*/
|
|
671
|
+
interface CliOptions {
|
|
672
|
+
spec: string;
|
|
673
|
+
port?: number;
|
|
674
|
+
hostname?: string;
|
|
675
|
+
seed?: string;
|
|
676
|
+
cors?: boolean;
|
|
677
|
+
debug?: boolean;
|
|
678
|
+
fakerSeed?: number;
|
|
679
|
+
errors?: boolean;
|
|
680
|
+
watch?: boolean;
|
|
681
|
+
admin?: boolean;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
/**
|
|
685
|
+
* Running CLI server instance
|
|
686
|
+
*/
|
|
687
|
+
interface CliServer {
|
|
688
|
+
server: import("node:http").Server;
|
|
689
|
+
port: number;
|
|
690
|
+
hostname: string;
|
|
691
|
+
close(): void;
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
// >>> schmock ambient namespace <<<
|
|
2
697
|
/**
|
|
3
698
|
* Create a new Schmock mock instance with callable API.
|
|
4
699
|
*
|
package/package.json
CHANGED
package/dist/schmock.d.ts
DELETED
|
@@ -1,692 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Schmock - Schema-driven mock API generator with callable API
|
|
3
|
-
* @packageDocumentation
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
declare namespace Schmock {
|
|
7
|
-
type JSONSchema7 = import("json-schema").JSONSchema7;
|
|
8
|
-
/**
|
|
9
|
-
* HTTP methods supported by Schmock
|
|
10
|
-
*/
|
|
11
|
-
type HttpMethod =
|
|
12
|
-
| "GET"
|
|
13
|
-
| "POST"
|
|
14
|
-
| "PUT"
|
|
15
|
-
| "DELETE"
|
|
16
|
-
| "PATCH"
|
|
17
|
-
| "HEAD"
|
|
18
|
-
| "OPTIONS";
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Route key format: 'METHOD /path'
|
|
22
|
-
*
|
|
23
|
-
* @example
|
|
24
|
-
* 'GET /users'
|
|
25
|
-
* 'POST /users/:id'
|
|
26
|
-
* 'DELETE /api/posts/:postId/comments/:commentId'
|
|
27
|
-
*/
|
|
28
|
-
type RouteKey = `${HttpMethod} ${string}`;
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Plugin interface for extending Schmock functionality
|
|
32
|
-
*/
|
|
33
|
-
interface Plugin {
|
|
34
|
-
/** Unique plugin identifier */
|
|
35
|
-
name: string;
|
|
36
|
-
/** Plugin version (semver) */
|
|
37
|
-
version?: string;
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Called once when the plugin is added via .pipe()
|
|
41
|
-
* Use this to register routes or configure the instance at setup time
|
|
42
|
-
* @param instance - The callable mock instance
|
|
43
|
-
*/
|
|
44
|
-
install?(instance: CallableMockInstance): void;
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Process the request through this plugin
|
|
48
|
-
* First plugin to set response becomes the generator, others transform
|
|
49
|
-
* @param context - Plugin context with request details
|
|
50
|
-
* @param response - Response from previous plugin (if any)
|
|
51
|
-
* @returns Updated context and response
|
|
52
|
-
*/
|
|
53
|
-
process(context: PluginContext, response?: unknown): PluginResult | Promise<PluginResult>;
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Called when an error occurs
|
|
57
|
-
* Can handle, transform, or suppress errors
|
|
58
|
-
* @param error - The error that occurred
|
|
59
|
-
* @param context - Plugin context
|
|
60
|
-
* @returns Modified error, response data, or void to continue error propagation
|
|
61
|
-
*/
|
|
62
|
-
onError?(error: Error, context: PluginContext): Error | ResponseResult | void | Promise<Error | ResponseResult | void>;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Alias for response body type
|
|
67
|
-
*/
|
|
68
|
-
type ResponseBody = unknown;
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Result returned by plugin process method
|
|
72
|
-
*/
|
|
73
|
-
interface PluginResult {
|
|
74
|
-
/** Updated context */
|
|
75
|
-
context: PluginContext;
|
|
76
|
-
/** Response data (if generated/modified) */
|
|
77
|
-
response?: unknown;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Context passed through plugin pipeline
|
|
82
|
-
*/
|
|
83
|
-
interface PluginContext {
|
|
84
|
-
/** Request path */
|
|
85
|
-
path: string;
|
|
86
|
-
/** Matched route configuration */
|
|
87
|
-
route: RouteConfig;
|
|
88
|
-
/** HTTP method */
|
|
89
|
-
method: HttpMethod;
|
|
90
|
-
/** Route parameters */
|
|
91
|
-
params: Record<string, string>;
|
|
92
|
-
/** Query parameters */
|
|
93
|
-
query: Record<string, string>;
|
|
94
|
-
/** Request headers */
|
|
95
|
-
headers: Record<string, string>;
|
|
96
|
-
/** Request body */
|
|
97
|
-
body?: unknown;
|
|
98
|
-
/** Shared state between plugins for this request */
|
|
99
|
-
state: Map<string, unknown>;
|
|
100
|
-
/** Route-specific state */
|
|
101
|
-
routeState?: Record<string, unknown>;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// ===== Callable API Types =====
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Global configuration options for the mock instance
|
|
108
|
-
*/
|
|
109
|
-
interface GlobalConfig {
|
|
110
|
-
/** Base path prefix for all routes */
|
|
111
|
-
namespace?: string;
|
|
112
|
-
/** Response delay in ms, or [min, max] for random delay */
|
|
113
|
-
delay?: number | [number, number];
|
|
114
|
-
/** Enable debug mode for detailed logging */
|
|
115
|
-
debug?: boolean;
|
|
116
|
-
/** Initial shared state object */
|
|
117
|
-
state?: Record<string, unknown>;
|
|
118
|
-
/**
|
|
119
|
-
* Maximum number of requests retained in history (FIFO eviction).
|
|
120
|
-
* Defaults to unbounded; set this to cap memory growth in long-running servers.
|
|
121
|
-
*/
|
|
122
|
-
maxHistorySize?: number;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Route-specific configuration options
|
|
127
|
-
*/
|
|
128
|
-
interface RouteConfig {
|
|
129
|
-
/** MIME type for content type validation (auto-detected if not provided) */
|
|
130
|
-
contentType?: string;
|
|
131
|
-
/** Per-route response delay in ms, or [min, max] for random delay (overrides global) */
|
|
132
|
-
delay?: number | [number, number];
|
|
133
|
-
/** Additional route-specific options */
|
|
134
|
-
[key: string]: unknown;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Generator types that can be passed to route definitions
|
|
139
|
-
*/
|
|
140
|
-
type Generator =
|
|
141
|
-
| GeneratorFunction
|
|
142
|
-
| StaticData
|
|
143
|
-
| JSONSchema7;
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* Function that generates responses
|
|
147
|
-
*/
|
|
148
|
-
type GeneratorFunction = (context: RequestContext) => ResponseResult | Promise<ResponseResult>;
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* Static data (non-function) that gets returned as-is
|
|
152
|
-
*/
|
|
153
|
-
type StaticData = string | number | boolean | null | undefined | Record<string, unknown> | unknown[];
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* Context passed to generator functions
|
|
157
|
-
*/
|
|
158
|
-
interface RequestContext {
|
|
159
|
-
/** HTTP method */
|
|
160
|
-
method: HttpMethod;
|
|
161
|
-
/** Request path */
|
|
162
|
-
path: string;
|
|
163
|
-
/** Route parameters (e.g., :id) */
|
|
164
|
-
params: Record<string, string>;
|
|
165
|
-
/** Query string parameters */
|
|
166
|
-
query: Record<string, string>;
|
|
167
|
-
/** Request headers */
|
|
168
|
-
headers: Record<string, string>;
|
|
169
|
-
/** Request body (for POST, PUT, PATCH) */
|
|
170
|
-
body?: unknown;
|
|
171
|
-
/** Shared mutable state */
|
|
172
|
-
state: Record<string, unknown>;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
/**
|
|
176
|
-
* Response result types:
|
|
177
|
-
* - Any value: returns as 200 OK
|
|
178
|
-
* - [status, body]: custom status with body
|
|
179
|
-
* - [status, body, headers]: custom status, body, and headers
|
|
180
|
-
*/
|
|
181
|
-
type ResponseResult =
|
|
182
|
-
| ResponseBody
|
|
183
|
-
| [number, unknown]
|
|
184
|
-
| [number, unknown, Record<string, string>];
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
* Response object returned by handle method
|
|
188
|
-
*/
|
|
189
|
-
interface Response {
|
|
190
|
-
status: number;
|
|
191
|
-
body: unknown;
|
|
192
|
-
headers: Record<string, string>;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* Options for handle method
|
|
197
|
-
*/
|
|
198
|
-
interface RequestOptions {
|
|
199
|
-
headers?: Record<string, string>;
|
|
200
|
-
body?: unknown;
|
|
201
|
-
query?: Record<string, string>;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
/**
|
|
205
|
-
* Record of a single request handled by the mock
|
|
206
|
-
*/
|
|
207
|
-
interface RequestRecord {
|
|
208
|
-
/** HTTP method */
|
|
209
|
-
method: HttpMethod;
|
|
210
|
-
/** Request path (without namespace) */
|
|
211
|
-
path: string;
|
|
212
|
-
/** Extracted route parameters */
|
|
213
|
-
params: Record<string, string>;
|
|
214
|
-
/** Query parameters */
|
|
215
|
-
query: Record<string, string>;
|
|
216
|
-
/** Request headers */
|
|
217
|
-
headers: Record<string, string>;
|
|
218
|
-
/** Request body */
|
|
219
|
-
body: unknown;
|
|
220
|
-
/** Unix timestamp (ms) when request was handled */
|
|
221
|
-
timestamp: number;
|
|
222
|
-
/** Response returned for this request */
|
|
223
|
-
response: { status: number; body: unknown };
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
/**
|
|
227
|
-
* Main callable mock instance interface
|
|
228
|
-
*/
|
|
229
|
-
interface CallableMockInstance {
|
|
230
|
-
/**
|
|
231
|
-
* Define a route by calling the instance directly
|
|
232
|
-
*
|
|
233
|
-
* @param route - Route pattern in format 'METHOD /path'
|
|
234
|
-
* @param generator - Response generator (function, static data, or schema)
|
|
235
|
-
* @param config - Route-specific configuration
|
|
236
|
-
* @returns The same instance for method chaining
|
|
237
|
-
*
|
|
238
|
-
* @example
|
|
239
|
-
* ```typescript
|
|
240
|
-
* const mock = schmock()
|
|
241
|
-
* mock('GET /users', () => [...users], { contentType: 'application/json' })
|
|
242
|
-
* mock('POST /users', userData, { contentType: 'application/json' })
|
|
243
|
-
* ```
|
|
244
|
-
*/
|
|
245
|
-
(route: RouteKey, generator: Generator, config?: RouteConfig): CallableMockInstance;
|
|
246
|
-
|
|
247
|
-
/**
|
|
248
|
-
* Add a plugin to the pipeline
|
|
249
|
-
*
|
|
250
|
-
* @param plugin - Plugin to add to the pipeline
|
|
251
|
-
* @returns The same instance for method chaining
|
|
252
|
-
*
|
|
253
|
-
* @example
|
|
254
|
-
* ```typescript
|
|
255
|
-
* mock('GET /users', generator, config)
|
|
256
|
-
* .pipe(authPlugin())
|
|
257
|
-
* .pipe(corsPlugin())
|
|
258
|
-
* ```
|
|
259
|
-
*/
|
|
260
|
-
pipe(plugin: Plugin): CallableMockInstance;
|
|
261
|
-
|
|
262
|
-
/**
|
|
263
|
-
* Handle a request and return a response
|
|
264
|
-
*
|
|
265
|
-
* @param method - HTTP method
|
|
266
|
-
* @param path - Request path
|
|
267
|
-
* @param options - Request options (headers, body, query)
|
|
268
|
-
* @returns Promise resolving to response object
|
|
269
|
-
*
|
|
270
|
-
* @example
|
|
271
|
-
* ```typescript
|
|
272
|
-
* const response = await mock.handle('GET', '/users', {
|
|
273
|
-
* headers: { 'Authorization': 'Bearer token' }
|
|
274
|
-
* })
|
|
275
|
-
* ```
|
|
276
|
-
*/
|
|
277
|
-
handle(method: HttpMethod, path: string, options?: RequestOptions): Promise<Response>;
|
|
278
|
-
|
|
279
|
-
// ===== Request Spy / History API =====
|
|
280
|
-
|
|
281
|
-
/**
|
|
282
|
-
* Get all recorded requests, optionally filtered by method and path
|
|
283
|
-
*
|
|
284
|
-
* @param method - Filter by HTTP method
|
|
285
|
-
* @param path - Filter by request path
|
|
286
|
-
* @returns Array of request records
|
|
287
|
-
*/
|
|
288
|
-
history(): RequestRecord[];
|
|
289
|
-
history(method: HttpMethod, path: string): RequestRecord[];
|
|
290
|
-
|
|
291
|
-
/**
|
|
292
|
-
* Check if any request was made, optionally for a specific route
|
|
293
|
-
*
|
|
294
|
-
* @param method - Filter by HTTP method
|
|
295
|
-
* @param path - Filter by request path
|
|
296
|
-
* @returns true if at least one matching request was recorded
|
|
297
|
-
*/
|
|
298
|
-
called(): boolean;
|
|
299
|
-
called(method: HttpMethod, path: string): boolean;
|
|
300
|
-
|
|
301
|
-
/**
|
|
302
|
-
* Get the number of recorded requests, optionally for a specific route
|
|
303
|
-
*
|
|
304
|
-
* @param method - Filter by HTTP method
|
|
305
|
-
* @param path - Filter by request path
|
|
306
|
-
* @returns Number of matching requests
|
|
307
|
-
*/
|
|
308
|
-
callCount(): number;
|
|
309
|
-
callCount(method: HttpMethod, path: string): number;
|
|
310
|
-
|
|
311
|
-
/**
|
|
312
|
-
* Get the most recent request, optionally for a specific route
|
|
313
|
-
*
|
|
314
|
-
* @param method - Filter by HTTP method
|
|
315
|
-
* @param path - Filter by request path
|
|
316
|
-
* @returns Most recent matching request record, or undefined
|
|
317
|
-
*/
|
|
318
|
-
lastRequest(): RequestRecord | undefined;
|
|
319
|
-
lastRequest(method: HttpMethod, path: string): RequestRecord | undefined;
|
|
320
|
-
|
|
321
|
-
// ===== Reset / Lifecycle =====
|
|
322
|
-
|
|
323
|
-
/**
|
|
324
|
-
* Clear all routes, state, plugins, and history
|
|
325
|
-
*/
|
|
326
|
-
reset(): void;
|
|
327
|
-
|
|
328
|
-
/**
|
|
329
|
-
* Clear only request history, keep routes and state
|
|
330
|
-
*/
|
|
331
|
-
resetHistory(): void;
|
|
332
|
-
|
|
333
|
-
/**
|
|
334
|
-
* Clear only state, keep routes and history
|
|
335
|
-
*/
|
|
336
|
-
resetState(): void;
|
|
337
|
-
|
|
338
|
-
// ===== Lifecycle Events =====
|
|
339
|
-
|
|
340
|
-
/**
|
|
341
|
-
* Register an event listener
|
|
342
|
-
*/
|
|
343
|
-
on<E extends SchmockEvent>(event: E, listener: (data: SchmockEventMap[E]) => void): CallableMockInstance;
|
|
344
|
-
|
|
345
|
-
/**
|
|
346
|
-
* Remove an event listener
|
|
347
|
-
*/
|
|
348
|
-
off<E extends SchmockEvent>(event: E, listener: (data: SchmockEventMap[E]) => void): CallableMockInstance;
|
|
349
|
-
|
|
350
|
-
// ===== Introspection =====
|
|
351
|
-
|
|
352
|
-
/**
|
|
353
|
-
* Get all registered routes as an array of route info objects
|
|
354
|
-
*/
|
|
355
|
-
getRoutes(): RouteInfo[];
|
|
356
|
-
|
|
357
|
-
/**
|
|
358
|
-
* Get the current shared state object
|
|
359
|
-
*/
|
|
360
|
-
getState(): Record<string, unknown>;
|
|
361
|
-
|
|
362
|
-
// ===== Standalone Server =====
|
|
363
|
-
|
|
364
|
-
/**
|
|
365
|
-
* Start a standalone HTTP server
|
|
366
|
-
*
|
|
367
|
-
* @param port - Port to listen on (0 for random)
|
|
368
|
-
* @param hostname - Hostname to bind to (default: "127.0.0.1")
|
|
369
|
-
* @returns Promise resolving to server info with actual port and hostname
|
|
370
|
-
* @throws If the server is already running
|
|
371
|
-
*/
|
|
372
|
-
listen(port?: number, hostname?: string): Promise<ServerInfo>;
|
|
373
|
-
|
|
374
|
-
/**
|
|
375
|
-
* Stop the standalone server (idempotent, no-op if not running)
|
|
376
|
-
*/
|
|
377
|
-
close(): void;
|
|
378
|
-
|
|
379
|
-
// ===== Fetch Interceptor =====
|
|
380
|
-
|
|
381
|
-
/**
|
|
382
|
-
* Intercept globalThis.fetch and route requests through this mock.
|
|
383
|
-
* Client-side equivalent of listen().
|
|
384
|
-
*
|
|
385
|
-
* @param options - Intercept configuration
|
|
386
|
-
* @returns Handle with restore() to stop intercepting
|
|
387
|
-
* @throws If already intercepting (call restore() first)
|
|
388
|
-
*/
|
|
389
|
-
intercept(options?: InterceptOptions): InterceptHandle;
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
/**
|
|
393
|
-
* Information about a running standalone server
|
|
394
|
-
*/
|
|
395
|
-
interface ServerInfo {
|
|
396
|
-
/** Port the server is listening on */
|
|
397
|
-
port: number;
|
|
398
|
-
/** Hostname the server is bound to */
|
|
399
|
-
hostname: string;
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
// ===== Response Helpers =====
|
|
403
|
-
|
|
404
|
-
interface PaginateOptions {
|
|
405
|
-
page?: number;
|
|
406
|
-
pageSize?: number;
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
interface PaginatedResponse<T> {
|
|
410
|
-
data: T[];
|
|
411
|
-
page: number;
|
|
412
|
-
pageSize: number;
|
|
413
|
-
total: number;
|
|
414
|
-
totalPages: number;
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
// ===== Adapter Types =====
|
|
418
|
-
|
|
419
|
-
interface AdapterRequest {
|
|
420
|
-
method: string;
|
|
421
|
-
path: string;
|
|
422
|
-
headers: Record<string, string>;
|
|
423
|
-
body?: unknown;
|
|
424
|
-
query: Record<string, string>;
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
interface AdapterResponse {
|
|
428
|
-
status: number;
|
|
429
|
-
body: unknown;
|
|
430
|
-
headers: Record<string, string>;
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
interface InterceptOptions {
|
|
434
|
-
/**
|
|
435
|
-
* Only intercept URLs matching this base.
|
|
436
|
-
*
|
|
437
|
-
* Two modes:
|
|
438
|
-
* - Path form ("/api"): match request pathnames whose prefix is the
|
|
439
|
-
* base path (with a segment-boundary check, so "/api" never
|
|
440
|
-
* matches "/apiv2").
|
|
441
|
-
* - Origin form ("https://api.example.com" or
|
|
442
|
-
* "https://api.example.com/v1"): require the request origin to
|
|
443
|
-
* match the base origin AND, if a base path is present, the
|
|
444
|
-
* request pathname to start with it. Relative-URL fetches
|
|
445
|
-
* (no origin) won't match an origin-form base.
|
|
446
|
-
*/
|
|
447
|
-
baseUrl?: string;
|
|
448
|
-
/** Pass unmatched routes to real fetch (default: true) */
|
|
449
|
-
passthrough?: boolean;
|
|
450
|
-
/** Modify request before Schmock handles it */
|
|
451
|
-
beforeRequest?: (
|
|
452
|
-
request: AdapterRequest,
|
|
453
|
-
) => AdapterRequest | void | Promise<AdapterRequest | void>;
|
|
454
|
-
/** Modify response before returning to caller */
|
|
455
|
-
beforeResponse?: (
|
|
456
|
-
response: AdapterResponse,
|
|
457
|
-
request: AdapterRequest,
|
|
458
|
-
) => AdapterResponse | void | Promise<AdapterResponse | void>;
|
|
459
|
-
/** Format errors into custom response bodies */
|
|
460
|
-
errorFormatter?: (error: Error) => unknown;
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
interface InterceptHandle {
|
|
464
|
-
/** Stop intercepting and restore original fetch */
|
|
465
|
-
restore(): void;
|
|
466
|
-
/** Whether this interceptor is currently active */
|
|
467
|
-
readonly active: boolean;
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
// ===== Lifecycle Events =====
|
|
471
|
-
|
|
472
|
-
interface RequestStartEvent {
|
|
473
|
-
method: HttpMethod;
|
|
474
|
-
path: string;
|
|
475
|
-
headers: Record<string, string>;
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
interface RequestMatchEvent {
|
|
479
|
-
method: HttpMethod;
|
|
480
|
-
path: string;
|
|
481
|
-
routePath: string;
|
|
482
|
-
params: Record<string, string>;
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
interface RequestNotFoundEvent {
|
|
486
|
-
method: HttpMethod;
|
|
487
|
-
path: string;
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
interface RequestEndEvent {
|
|
491
|
-
method: HttpMethod;
|
|
492
|
-
path: string;
|
|
493
|
-
status: number;
|
|
494
|
-
duration: number;
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
type SchmockEventMap = {
|
|
498
|
-
"request:start": RequestStartEvent;
|
|
499
|
-
"request:match": RequestMatchEvent;
|
|
500
|
-
"request:notfound": RequestNotFoundEvent;
|
|
501
|
-
"request:end": RequestEndEvent;
|
|
502
|
-
};
|
|
503
|
-
|
|
504
|
-
type SchmockEvent = keyof SchmockEventMap;
|
|
505
|
-
|
|
506
|
-
// ===== Introspection Types =====
|
|
507
|
-
|
|
508
|
-
interface RouteInfo {
|
|
509
|
-
method: HttpMethod;
|
|
510
|
-
path: string;
|
|
511
|
-
hasParams: boolean;
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
// ===== OpenAPI Plugin Types =====
|
|
515
|
-
|
|
516
|
-
/**
|
|
517
|
-
* Per-resource configuration override for OpenAPI plugin
|
|
518
|
-
*/
|
|
519
|
-
interface ResourceOverride {
|
|
520
|
-
/** Override: which property in list response holds the items array (e.g. "data") */
|
|
521
|
-
listWrapProperty?: string;
|
|
522
|
-
/** Override: force flat array for list (ignores any wrapper in the spec) */
|
|
523
|
-
listFlat?: boolean;
|
|
524
|
-
/** Override: JSON Schema for error responses (404, etc.) */
|
|
525
|
-
errorSchema?: JSONSchema7;
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
/**
|
|
529
|
-
* Response header definition from an OpenAPI spec
|
|
530
|
-
*/
|
|
531
|
-
interface ResponseHeaderDef {
|
|
532
|
-
schema?: JSONSchema7;
|
|
533
|
-
description: string;
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
/**
|
|
537
|
-
* Per-operation metadata auto-detected from spec or set via overrides
|
|
538
|
-
*/
|
|
539
|
-
interface CrudOperationMeta {
|
|
540
|
-
/** Full success response schema (wrapper + items) */
|
|
541
|
-
responseSchema?: JSONSchema7;
|
|
542
|
-
/** Response headers from spec */
|
|
543
|
-
responseHeaders?: Record<string, ResponseHeaderDef>;
|
|
544
|
-
/** Error response schemas keyed by status code */
|
|
545
|
-
errorSchemas?: Map<number, JSONSchema7>;
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
// ===== Faker Plugin Types =====
|
|
549
|
-
|
|
550
|
-
/**
|
|
551
|
-
* Context for schema-based data generation
|
|
552
|
-
*/
|
|
553
|
-
interface SchemaGenerationContext {
|
|
554
|
-
schema: JSONSchema7;
|
|
555
|
-
count?: number;
|
|
556
|
-
overrides?: Record<string, unknown>;
|
|
557
|
-
params?: Record<string, string>;
|
|
558
|
-
state?: Record<string, unknown>;
|
|
559
|
-
query?: Record<string, string>;
|
|
560
|
-
seed?: number;
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
/**
|
|
564
|
-
* Options for the faker plugin
|
|
565
|
-
*/
|
|
566
|
-
interface FakerPluginOptions {
|
|
567
|
-
schema: JSONSchema7;
|
|
568
|
-
count?: number;
|
|
569
|
-
overrides?: Record<string, unknown>;
|
|
570
|
-
seed?: number;
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
// ===== Express Adapter Types =====
|
|
574
|
-
|
|
575
|
-
/**
|
|
576
|
-
* Override parts of a request before Schmock handles it
|
|
577
|
-
*/
|
|
578
|
-
interface AdapterRequestOverride {
|
|
579
|
-
method?: string;
|
|
580
|
-
path?: string;
|
|
581
|
-
headers?: Record<string, string>;
|
|
582
|
-
body?: unknown;
|
|
583
|
-
query?: Record<string, string>;
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
/**
|
|
587
|
-
* Configuration options for Express adapter
|
|
588
|
-
*/
|
|
589
|
-
interface ExpressAdapterOptions {
|
|
590
|
-
errorFormatter?: (error: Error, req: unknown) => unknown;
|
|
591
|
-
passErrorsToNext?: boolean;
|
|
592
|
-
transformHeaders?: (
|
|
593
|
-
headers: Record<string, string | string[] | undefined>,
|
|
594
|
-
) => Record<string, string>;
|
|
595
|
-
transformQuery?: (query: Record<string, unknown>) => Record<string, string>;
|
|
596
|
-
beforeRequest?: (
|
|
597
|
-
req: unknown,
|
|
598
|
-
res: unknown,
|
|
599
|
-
) =>
|
|
600
|
-
| AdapterRequestOverride
|
|
601
|
-
| undefined
|
|
602
|
-
| Promise<AdapterRequestOverride | undefined>;
|
|
603
|
-
beforeResponse?: (
|
|
604
|
-
response: Response,
|
|
605
|
-
req: unknown,
|
|
606
|
-
res: unknown,
|
|
607
|
-
) => Response | undefined | Promise<Response | undefined>;
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
// ===== Angular Adapter Types =====
|
|
611
|
-
|
|
612
|
-
/**
|
|
613
|
-
* Configuration options for Angular adapter
|
|
614
|
-
*/
|
|
615
|
-
interface AngularAdapterOptions {
|
|
616
|
-
baseUrl?: string;
|
|
617
|
-
passthrough?: boolean;
|
|
618
|
-
errorFormatter?: (error: Error, request: unknown) => unknown;
|
|
619
|
-
transformRequest?: (request: unknown) => AdapterRequestOverride;
|
|
620
|
-
transformResponse?: (
|
|
621
|
-
response: Response,
|
|
622
|
-
request: unknown,
|
|
623
|
-
) => Response;
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
// ===== OpenAPI Plugin Options =====
|
|
627
|
-
|
|
628
|
-
/**
|
|
629
|
-
* Options for the OpenAPI plugin
|
|
630
|
-
*/
|
|
631
|
-
interface OpenApiOptions {
|
|
632
|
-
spec: string | object;
|
|
633
|
-
seed?: SeedConfig;
|
|
634
|
-
validateRequests?: boolean;
|
|
635
|
-
validateResponses?: boolean;
|
|
636
|
-
queryFeatures?: {
|
|
637
|
-
pagination?: boolean;
|
|
638
|
-
sorting?: boolean;
|
|
639
|
-
filtering?: boolean;
|
|
640
|
-
};
|
|
641
|
-
resources?: Record<string, ResourceOverride>;
|
|
642
|
-
debug?: boolean;
|
|
643
|
-
fakerSeed?: number;
|
|
644
|
-
security?: boolean;
|
|
645
|
-
/** Replace response schemas for specific routes. Key format: "METHOD /path" or "METHOD /path STATUS" */
|
|
646
|
-
schemas?: Record<string, import("json-schema").JSONSchema7>;
|
|
647
|
-
/** Called before generating a response body. Return a schema to replace the original, or void to keep it. */
|
|
648
|
-
onSchema?: (
|
|
649
|
-
schema: import("json-schema").JSONSchema7,
|
|
650
|
-
context: { method: string; path: string; params: Record<string, string>; query: Record<string, string>; headers: Record<string, string> },
|
|
651
|
-
) => import("json-schema").JSONSchema7 | undefined;
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
/**
|
|
655
|
-
* Seed data source: inline array, file path, or auto-generate count
|
|
656
|
-
*/
|
|
657
|
-
type SeedSource = unknown[] | string | { count: number };
|
|
658
|
-
|
|
659
|
-
/**
|
|
660
|
-
* Seed configuration mapping resource names to seed sources
|
|
661
|
-
*/
|
|
662
|
-
type SeedConfig = Record<string, SeedSource>;
|
|
663
|
-
|
|
664
|
-
// ===== CLI Types =====
|
|
665
|
-
|
|
666
|
-
/**
|
|
667
|
-
* Options for the CLI server
|
|
668
|
-
*/
|
|
669
|
-
interface CliOptions {
|
|
670
|
-
spec: string;
|
|
671
|
-
port?: number;
|
|
672
|
-
hostname?: string;
|
|
673
|
-
seed?: string;
|
|
674
|
-
cors?: boolean;
|
|
675
|
-
debug?: boolean;
|
|
676
|
-
fakerSeed?: number;
|
|
677
|
-
errors?: boolean;
|
|
678
|
-
watch?: boolean;
|
|
679
|
-
admin?: boolean;
|
|
680
|
-
}
|
|
681
|
-
|
|
682
|
-
/**
|
|
683
|
-
* Running CLI server instance
|
|
684
|
-
*/
|
|
685
|
-
interface CliServer {
|
|
686
|
-
server: import("node:http").Server;
|
|
687
|
-
port: number;
|
|
688
|
-
hostname: string;
|
|
689
|
-
close(): void;
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
}
|