@schmock/core 1.0.0 → 1.0.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/builder.d.ts +5 -4
- package/dist/builder.d.ts.map +1 -1
- package/dist/builder.js +2 -2
- package/dist/errors.d.ts +3 -2
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +42 -1
- package/dist/parser.d.ts +2 -1
- package/dist/parser.d.ts.map +1 -1
- package/dist/parser.js +18 -2
- package/dist/types.d.ts +210 -14
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +0 -1
- package/package.json +5 -5
- package/src/builder.ts +35 -27
- package/src/index.ts +20 -13
- package/src/parser.ts +22 -3
- package/src/types.ts +259 -17
package/dist/builder.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Generator, GlobalConfig, HttpMethod, Plugin, RequestOptions, Response, RouteConfig, RouteKey } from "./types.js";
|
|
1
2
|
/**
|
|
2
3
|
* Callable mock instance that implements the new API.
|
|
3
4
|
*
|
|
@@ -8,10 +9,10 @@ export declare class CallableMockInstance {
|
|
|
8
9
|
private routes;
|
|
9
10
|
private plugins;
|
|
10
11
|
private logger;
|
|
11
|
-
constructor(globalConfig?:
|
|
12
|
-
defineRoute(route:
|
|
13
|
-
pipe(plugin:
|
|
14
|
-
handle(method:
|
|
12
|
+
constructor(globalConfig?: GlobalConfig);
|
|
13
|
+
defineRoute(route: RouteKey, generator: Generator, config: RouteConfig): this;
|
|
14
|
+
pipe(plugin: Plugin): this;
|
|
15
|
+
handle(method: HttpMethod, path: string, options?: RequestOptions): Promise<Response>;
|
|
15
16
|
/**
|
|
16
17
|
* Apply configured response delay
|
|
17
18
|
* Supports both fixed delays and random delays within a range
|
package/dist/builder.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../src/builder.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../src/builder.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EACV,SAAS,EAET,YAAY,EACZ,UAAU,EACV,MAAM,EAGN,cAAc,EACd,QAAQ,EACR,WAAW,EACX,QAAQ,EACT,MAAM,YAAY,CAAC;AA4CpB;;;;GAIG;AACH,qBAAa,oBAAoB;IAKnB,OAAO,CAAC,YAAY;IAJhC,OAAO,CAAC,MAAM,CAA+B;IAC7C,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,MAAM,CAAc;gBAER,YAAY,GAAE,YAAiB;IAanD,WAAW,CACT,KAAK,EAAE,QAAQ,EACf,SAAS,EAAE,SAAS,EACpB,MAAM,EAAE,WAAW,GAClB,IAAI;IA4DP,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAepB,MAAM,CACV,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,QAAQ,CAAC;IAiLpB;;;;OAIG;YACW,UAAU;IAcxB;;;;;;;OAOG;IACH,OAAO,CAAC,aAAa;IA0DrB;;;;;;;;;;OAUG;YACW,iBAAiB;IAmF/B;;;;;;;;OAQG;IACH,OAAO,CAAC,SAAS;IA+BjB;;;;;;;OAOG;IACH,OAAO,CAAC,aAAa;CActB"}
|
package/dist/builder.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { PluginError, RouteDefinitionError, RouteNotFoundError, SchmockError, } from "./errors";
|
|
2
|
-
import { parseRouteKey } from "./parser";
|
|
1
|
+
import { PluginError, RouteDefinitionError, RouteNotFoundError, SchmockError, } from "./errors.js";
|
|
2
|
+
import { parseRouteKey } from "./parser.js";
|
|
3
3
|
/**
|
|
4
4
|
* Debug logger that respects debug mode configuration
|
|
5
5
|
*/
|
package/dist/errors.d.ts
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export declare class SchmockError extends Error {
|
|
5
5
|
readonly code: string;
|
|
6
|
-
readonly context?: unknown;
|
|
7
|
-
constructor(message: string, code: string, context?: unknown);
|
|
6
|
+
readonly context?: unknown | undefined;
|
|
7
|
+
constructor(message: string, code: string, context?: unknown | undefined);
|
|
8
8
|
}
|
|
9
9
|
/**
|
|
10
10
|
* Error thrown when a route is not found
|
|
@@ -54,3 +54,4 @@ export declare class SchemaGenerationError extends SchmockError {
|
|
|
54
54
|
export declare class ResourceLimitError extends SchmockError {
|
|
55
55
|
constructor(resource: string, limit: number, actual?: number);
|
|
56
56
|
}
|
|
57
|
+
//# sourceMappingURL=errors.d.ts.map
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { CallableMockInstance, GlobalConfig } from "./types.js";
|
|
1
2
|
/**
|
|
2
3
|
* Create a new Schmock mock instance with callable API.
|
|
3
4
|
*
|
|
@@ -21,7 +22,7 @@
|
|
|
21
22
|
* @param config Optional global configuration
|
|
22
23
|
* @returns A callable mock instance
|
|
23
24
|
*/
|
|
24
|
-
export declare function schmock(config?:
|
|
25
|
-
export { PluginError, ResourceLimitError, ResponseGenerationError, RouteDefinitionError, RouteNotFoundError, RouteParseError, SchemaGenerationError, SchemaValidationError, SchmockError, } from "./errors";
|
|
26
|
-
export type { CallableMockInstance, Generator, GeneratorFunction, GlobalConfig, HttpMethod, Plugin, PluginContext, PluginResult, RequestContext, RequestOptions, Response, ResponseResult, RouteConfig, RouteKey, } from "./types";
|
|
25
|
+
export declare function schmock(config?: GlobalConfig): CallableMockInstance;
|
|
26
|
+
export { PluginError, ResourceLimitError, ResponseGenerationError, RouteDefinitionError, RouteNotFoundError, RouteParseError, SchemaGenerationError, SchemaValidationError, SchmockError, } from "./errors.js";
|
|
27
|
+
export type { CallableMockInstance, Generator, GeneratorFunction, GlobalConfig, HttpMethod, Plugin, PluginContext, PluginResult, RequestContext, RequestOptions, Response, ResponseResult, RouteConfig, RouteKey, } from "./types.js";
|
|
27
28
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,oBAAoB,EAEpB,YAAY,EAIb,MAAM,YAAY,CAAC;AAEpB;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,OAAO,CAAC,MAAM,CAAC,EAAE,YAAY,GAAG,oBAAoB,CAsBnE;AAGD,OAAO,EACL,WAAW,EACX,kBAAkB,EAClB,uBAAuB,EACvB,oBAAoB,EACpB,kBAAkB,EAClB,eAAe,EACf,qBAAqB,EACrB,qBAAqB,EACrB,YAAY,GACb,MAAM,aAAa,CAAC;AAGrB,YAAY,EACV,oBAAoB,EACpB,SAAS,EACT,iBAAiB,EACjB,YAAY,EACZ,UAAU,EACV,MAAM,EACN,aAAa,EACb,YAAY,EACZ,cAAc,EACd,cAAc,EACd,QAAQ,EACR,cAAc,EACd,WAAW,EACX,QAAQ,GACT,MAAM,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1 +1,42 @@
|
|
|
1
|
-
|
|
1
|
+
import { CallableMockInstance as CallableMockInstanceImpl } from "./builder.js";
|
|
2
|
+
/**
|
|
3
|
+
* Create a new Schmock mock instance with callable API.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```typescript
|
|
7
|
+
* // New callable API (default)
|
|
8
|
+
* const mock = schmock({ debug: true })
|
|
9
|
+
* mock('GET /users', () => [{ id: 1, name: 'John' }])
|
|
10
|
+
* .pipe(authPlugin())
|
|
11
|
+
*
|
|
12
|
+
* const response = await mock.handle('GET', '/users')
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* // Simple usage with defaults
|
|
18
|
+
* const mock = schmock()
|
|
19
|
+
* mock('GET /users', [{ id: 1, name: 'John' }])
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* @param config Optional global configuration
|
|
23
|
+
* @returns A callable mock instance
|
|
24
|
+
*/
|
|
25
|
+
export function schmock(config) {
|
|
26
|
+
// Always use new callable API
|
|
27
|
+
const instance = new CallableMockInstanceImpl(config || {});
|
|
28
|
+
// Create a callable function that wraps the instance
|
|
29
|
+
const callableInstance = ((route, generator, routeConfig = {}) => {
|
|
30
|
+
instance.defineRoute(route, generator, routeConfig);
|
|
31
|
+
return callableInstance; // Return the callable function for chaining
|
|
32
|
+
});
|
|
33
|
+
// Manually bind all instance methods to the callable function with proper return values
|
|
34
|
+
callableInstance.pipe = (plugin) => {
|
|
35
|
+
instance.pipe(plugin);
|
|
36
|
+
return callableInstance; // Return callable function for chaining
|
|
37
|
+
};
|
|
38
|
+
callableInstance.handle = instance.handle.bind(instance);
|
|
39
|
+
return callableInstance;
|
|
40
|
+
}
|
|
41
|
+
// Re-export errors
|
|
42
|
+
export { PluginError, ResourceLimitError, ResponseGenerationError, RouteDefinitionError, RouteNotFoundError, RouteParseError, SchemaGenerationError, SchemaValidationError, SchmockError, } from "./errors.js";
|
package/dist/parser.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { HttpMethod } from "./types";
|
|
1
|
+
import type { HttpMethod } from "./types.js";
|
|
2
2
|
export interface ParsedRoute {
|
|
3
3
|
method: HttpMethod;
|
|
4
4
|
path: string;
|
|
@@ -17,3 +17,4 @@ export interface ParsedRoute {
|
|
|
17
17
|
* // => { method: 'GET', path: '/users/:id', pattern: /^\/users\/([^/]+)$/, params: ['id'] }
|
|
18
18
|
*/
|
|
19
19
|
export declare function parseRouteKey(routeKey: string): ParsedRoute;
|
|
20
|
+
//# sourceMappingURL=parser.d.ts.map
|
package/dist/parser.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAgB7C,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,UAAU,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,CA4C3D"}
|
package/dist/parser.js
CHANGED
|
@@ -1,4 +1,16 @@
|
|
|
1
|
-
import { RouteParseError } from "./errors";
|
|
1
|
+
import { RouteParseError } from "./errors.js";
|
|
2
|
+
const HTTP_METHODS = [
|
|
3
|
+
"GET",
|
|
4
|
+
"POST",
|
|
5
|
+
"PUT",
|
|
6
|
+
"DELETE",
|
|
7
|
+
"PATCH",
|
|
8
|
+
"HEAD",
|
|
9
|
+
"OPTIONS",
|
|
10
|
+
];
|
|
11
|
+
function isHttpMethod(method) {
|
|
12
|
+
return HTTP_METHODS.includes(method);
|
|
13
|
+
}
|
|
2
14
|
/**
|
|
3
15
|
* Parse 'METHOD /path' route key format
|
|
4
16
|
*
|
|
@@ -31,8 +43,12 @@ export function parseRouteKey(routeKey) {
|
|
|
31
43
|
.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") // Escape special regex chars except :
|
|
32
44
|
.replace(/:([^/]+)/g, "([^/]+)"); // Replace :param with capture group
|
|
33
45
|
const pattern = new RegExp(`^${regexPath}$`);
|
|
46
|
+
// The regex guarantees method is valid, but we use the type guard for type safety
|
|
47
|
+
if (!isHttpMethod(method)) {
|
|
48
|
+
throw new RouteParseError(routeKey, `Invalid HTTP method: ${method}`);
|
|
49
|
+
}
|
|
34
50
|
return {
|
|
35
|
-
method
|
|
51
|
+
method,
|
|
36
52
|
path,
|
|
37
53
|
pattern,
|
|
38
54
|
params,
|
package/dist/types.d.ts
CHANGED
|
@@ -1,15 +1,211 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
export
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
1
|
+
/**
|
|
2
|
+
* JSON Schema type (simplified for core package)
|
|
3
|
+
* Full schema support available via @schmock/schema
|
|
4
|
+
*/
|
|
5
|
+
export interface JSONSchema {
|
|
6
|
+
type?: string | string[];
|
|
7
|
+
properties?: Record<string, JSONSchema>;
|
|
8
|
+
items?: JSONSchema | JSONSchema[];
|
|
9
|
+
required?: string[];
|
|
10
|
+
enum?: any[];
|
|
11
|
+
const?: any;
|
|
12
|
+
[key: string]: any;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* HTTP methods supported by Schmock
|
|
16
|
+
*/
|
|
17
|
+
export type HttpMethod = "GET" | "POST" | "PUT" | "DELETE" | "PATCH" | "HEAD" | "OPTIONS";
|
|
18
|
+
/**
|
|
19
|
+
* Route key format: 'METHOD /path'
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* 'GET /users'
|
|
23
|
+
* 'POST /users/:id'
|
|
24
|
+
* 'DELETE /api/posts/:postId/comments/:commentId'
|
|
25
|
+
*/
|
|
26
|
+
export type RouteKey = `${HttpMethod} ${string}`;
|
|
27
|
+
/**
|
|
28
|
+
* Plugin interface for extending Schmock functionality
|
|
29
|
+
*/
|
|
30
|
+
export interface Plugin {
|
|
31
|
+
/** Unique plugin identifier */
|
|
32
|
+
name: string;
|
|
33
|
+
/** Plugin version (semver) */
|
|
34
|
+
version?: string;
|
|
35
|
+
/**
|
|
36
|
+
* Process the request through this plugin
|
|
37
|
+
* First plugin to set response becomes the generator, others transform
|
|
38
|
+
* @param context - Plugin context with request details
|
|
39
|
+
* @param response - Response from previous plugin (if any)
|
|
40
|
+
* @returns Updated context and response
|
|
41
|
+
*/
|
|
42
|
+
process(context: PluginContext, response?: any): PluginResult | Promise<PluginResult>;
|
|
43
|
+
/**
|
|
44
|
+
* Called when an error occurs
|
|
45
|
+
* Can handle, transform, or suppress errors
|
|
46
|
+
* @param error - The error that occurred
|
|
47
|
+
* @param context - Plugin context
|
|
48
|
+
* @returns Modified error, response data, or void to continue error propagation
|
|
49
|
+
*/
|
|
50
|
+
onError?(error: Error, context: PluginContext): Error | ResponseResult | undefined | Promise<Error | ResponseResult | undefined>;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Result returned by plugin process method
|
|
54
|
+
*/
|
|
55
|
+
export interface PluginResult {
|
|
56
|
+
/** Updated context */
|
|
57
|
+
context: PluginContext;
|
|
58
|
+
/** Response data (if generated/modified) */
|
|
59
|
+
response?: any;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Context passed through plugin pipeline
|
|
63
|
+
*/
|
|
64
|
+
export interface PluginContext {
|
|
65
|
+
/** Request path */
|
|
66
|
+
path: string;
|
|
67
|
+
/** Matched route configuration */
|
|
68
|
+
route: any;
|
|
69
|
+
/** HTTP method */
|
|
70
|
+
method: HttpMethod;
|
|
71
|
+
/** Route parameters */
|
|
72
|
+
params: Record<string, string>;
|
|
73
|
+
/** Query parameters */
|
|
74
|
+
query: Record<string, string>;
|
|
75
|
+
/** Request headers */
|
|
76
|
+
headers: Record<string, string>;
|
|
77
|
+
/** Request body */
|
|
78
|
+
body?: any;
|
|
79
|
+
/** Shared state between plugins for this request */
|
|
80
|
+
state: Map<string, any>;
|
|
81
|
+
/** Route-specific state */
|
|
82
|
+
routeState?: any;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Global configuration options for the mock instance
|
|
86
|
+
*/
|
|
87
|
+
export interface GlobalConfig {
|
|
88
|
+
/** Base path prefix for all routes */
|
|
89
|
+
namespace?: string;
|
|
90
|
+
/** Response delay in ms, or [min, max] for random delay */
|
|
91
|
+
delay?: number | [number, number];
|
|
92
|
+
/** Enable debug mode for detailed logging */
|
|
93
|
+
debug?: boolean;
|
|
94
|
+
/** Initial shared state object */
|
|
95
|
+
state?: any;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Route-specific configuration options
|
|
99
|
+
*/
|
|
100
|
+
export interface RouteConfig {
|
|
101
|
+
/** MIME type for content type validation (auto-detected if not provided) */
|
|
102
|
+
contentType?: string;
|
|
103
|
+
/** Additional route-specific options */
|
|
104
|
+
[key: string]: any;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Generator types that can be passed to route definitions
|
|
108
|
+
*/
|
|
109
|
+
export type Generator = GeneratorFunction | StaticData | JSONSchema;
|
|
110
|
+
/**
|
|
111
|
+
* Function that generates responses
|
|
112
|
+
*/
|
|
113
|
+
export type GeneratorFunction = (context: RequestContext) => ResponseResult | Promise<ResponseResult>;
|
|
114
|
+
/**
|
|
115
|
+
* Static data (non-function) that gets returned as-is
|
|
116
|
+
*/
|
|
117
|
+
export type StaticData = any;
|
|
118
|
+
/**
|
|
119
|
+
* Context passed to generator functions
|
|
120
|
+
*/
|
|
121
|
+
export interface RequestContext {
|
|
122
|
+
/** HTTP method */
|
|
123
|
+
method: HttpMethod;
|
|
124
|
+
/** Request path */
|
|
125
|
+
path: string;
|
|
126
|
+
/** Route parameters (e.g., :id) */
|
|
127
|
+
params: Record<string, string>;
|
|
128
|
+
/** Query string parameters */
|
|
129
|
+
query: Record<string, string>;
|
|
130
|
+
/** Request headers */
|
|
131
|
+
headers: Record<string, string>;
|
|
132
|
+
/** Request body (for POST, PUT, PATCH) */
|
|
133
|
+
body?: any;
|
|
134
|
+
/** Shared mutable state */
|
|
135
|
+
state: any;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Response result types:
|
|
139
|
+
* - Any value: returns as 200 OK
|
|
140
|
+
* - [status, body]: custom status with body
|
|
141
|
+
* - [status, body, headers]: custom status, body, and headers
|
|
142
|
+
*/
|
|
143
|
+
export type ResponseResult = any | [number, any] | [number, any, Record<string, string>];
|
|
144
|
+
/**
|
|
145
|
+
* Response object returned by handle method
|
|
146
|
+
*/
|
|
147
|
+
export interface Response {
|
|
148
|
+
status: number;
|
|
149
|
+
body: any;
|
|
150
|
+
headers: Record<string, string>;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Options for handle method
|
|
154
|
+
*/
|
|
155
|
+
export interface RequestOptions {
|
|
156
|
+
headers?: Record<string, string>;
|
|
157
|
+
body?: any;
|
|
158
|
+
query?: Record<string, string>;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Main callable mock instance interface
|
|
162
|
+
*/
|
|
163
|
+
export interface CallableMockInstance {
|
|
164
|
+
/**
|
|
165
|
+
* Define a route by calling the instance directly
|
|
166
|
+
*
|
|
167
|
+
* @param route - Route pattern in format 'METHOD /path'
|
|
168
|
+
* @param generator - Response generator (function, static data, or schema)
|
|
169
|
+
* @param config - Route-specific configuration
|
|
170
|
+
* @returns The same instance for method chaining
|
|
171
|
+
*
|
|
172
|
+
* @example
|
|
173
|
+
* ```typescript
|
|
174
|
+
* const mock = schmock()
|
|
175
|
+
* mock('GET /users', () => [...users], { contentType: 'application/json' })
|
|
176
|
+
* mock('POST /users', userData, { contentType: 'application/json' })
|
|
177
|
+
* ```
|
|
178
|
+
*/
|
|
179
|
+
(route: RouteKey, generator: Generator, config?: RouteConfig): CallableMockInstance;
|
|
180
|
+
/**
|
|
181
|
+
* Add a plugin to the pipeline
|
|
182
|
+
*
|
|
183
|
+
* @param plugin - Plugin to add to the pipeline
|
|
184
|
+
* @returns The same instance for method chaining
|
|
185
|
+
*
|
|
186
|
+
* @example
|
|
187
|
+
* ```typescript
|
|
188
|
+
* mock('GET /users', generator, config)
|
|
189
|
+
* .pipe(authPlugin())
|
|
190
|
+
* .pipe(corsPlugin())
|
|
191
|
+
* ```
|
|
192
|
+
*/
|
|
193
|
+
pipe(plugin: Plugin): CallableMockInstance;
|
|
194
|
+
/**
|
|
195
|
+
* Handle a request and return a response
|
|
196
|
+
*
|
|
197
|
+
* @param method - HTTP method
|
|
198
|
+
* @param path - Request path
|
|
199
|
+
* @param options - Request options (headers, body, query)
|
|
200
|
+
* @returns Promise resolving to response object
|
|
201
|
+
*
|
|
202
|
+
* @example
|
|
203
|
+
* ```typescript
|
|
204
|
+
* const response = await mock.handle('GET', '/users', {
|
|
205
|
+
* headers: { 'Authorization': 'Bearer token' }
|
|
206
|
+
* })
|
|
207
|
+
* ```
|
|
208
|
+
*/
|
|
209
|
+
handle(method: HttpMethod, path: string, options?: RequestOptions): Promise<Response>;
|
|
210
|
+
}
|
|
15
211
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@schmock/core",
|
|
3
3
|
"description": "Core functionality for Schmock",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.2",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
@@ -18,16 +18,16 @@
|
|
|
18
18
|
"./package.json": "./package.json"
|
|
19
19
|
},
|
|
20
20
|
"scripts": {
|
|
21
|
-
"build": "
|
|
22
|
-
"build:
|
|
23
|
-
"build:types": "tsc -p tsconfig.json",
|
|
21
|
+
"build": "rm -rf dist tsconfig.tsbuildinfo && tsc --build",
|
|
22
|
+
"build:types": "rm -f tsconfig.tsbuildinfo && tsc --build",
|
|
24
23
|
"pretest": "rm -f src/*.js src/*.d.ts || true",
|
|
25
24
|
"test": "vitest",
|
|
26
25
|
"test:watch": "vitest --watch",
|
|
27
26
|
"pretest:bdd": "rm -f src/*.js src/*.d.ts || true",
|
|
28
27
|
"test:bdd": "vitest run --config vitest.config.bdd.ts",
|
|
29
28
|
"lint": "biome check src/*.ts",
|
|
30
|
-
"lint:fix": "biome check --write --unsafe src/*.ts"
|
|
29
|
+
"lint:fix": "biome check --write --unsafe src/*.ts",
|
|
30
|
+
"check:publish": "publint && attw --pack --ignore-rules cjs-resolves-to-esm"
|
|
31
31
|
},
|
|
32
32
|
"license": "MIT",
|
|
33
33
|
"devDependencies": {
|
package/src/builder.ts
CHANGED
|
@@ -3,8 +3,21 @@ import {
|
|
|
3
3
|
RouteDefinitionError,
|
|
4
4
|
RouteNotFoundError,
|
|
5
5
|
SchmockError,
|
|
6
|
-
} from "./errors";
|
|
7
|
-
import { parseRouteKey } from "./parser";
|
|
6
|
+
} from "./errors.js";
|
|
7
|
+
import { parseRouteKey } from "./parser.js";
|
|
8
|
+
import type {
|
|
9
|
+
Generator,
|
|
10
|
+
GeneratorFunction,
|
|
11
|
+
GlobalConfig,
|
|
12
|
+
HttpMethod,
|
|
13
|
+
Plugin,
|
|
14
|
+
PluginContext,
|
|
15
|
+
RequestContext,
|
|
16
|
+
RequestOptions,
|
|
17
|
+
Response,
|
|
18
|
+
RouteConfig,
|
|
19
|
+
RouteKey,
|
|
20
|
+
} from "./types.js";
|
|
8
21
|
|
|
9
22
|
/**
|
|
10
23
|
* Debug logger that respects debug mode configuration
|
|
@@ -42,10 +55,10 @@ class DebugLogger {
|
|
|
42
55
|
interface CompiledCallableRoute {
|
|
43
56
|
pattern: RegExp;
|
|
44
57
|
params: string[];
|
|
45
|
-
method:
|
|
58
|
+
method: HttpMethod;
|
|
46
59
|
path: string;
|
|
47
|
-
generator:
|
|
48
|
-
config:
|
|
60
|
+
generator: Generator;
|
|
61
|
+
config: RouteConfig;
|
|
49
62
|
}
|
|
50
63
|
|
|
51
64
|
/**
|
|
@@ -55,10 +68,10 @@ interface CompiledCallableRoute {
|
|
|
55
68
|
*/
|
|
56
69
|
export class CallableMockInstance {
|
|
57
70
|
private routes: CompiledCallableRoute[] = [];
|
|
58
|
-
private plugins:
|
|
71
|
+
private plugins: Plugin[] = [];
|
|
59
72
|
private logger: DebugLogger;
|
|
60
73
|
|
|
61
|
-
constructor(private globalConfig:
|
|
74
|
+
constructor(private globalConfig: GlobalConfig = {}) {
|
|
62
75
|
this.logger = new DebugLogger(globalConfig.debug || false);
|
|
63
76
|
if (globalConfig.debug) {
|
|
64
77
|
this.logger.log("config", "Debug mode enabled");
|
|
@@ -72,9 +85,9 @@ export class CallableMockInstance {
|
|
|
72
85
|
|
|
73
86
|
// Method for defining routes (called when instance is invoked)
|
|
74
87
|
defineRoute(
|
|
75
|
-
route:
|
|
76
|
-
generator:
|
|
77
|
-
config:
|
|
88
|
+
route: RouteKey,
|
|
89
|
+
generator: Generator,
|
|
90
|
+
config: RouteConfig,
|
|
78
91
|
): this {
|
|
79
92
|
// Auto-detect contentType if not provided
|
|
80
93
|
if (!config.contentType) {
|
|
@@ -135,7 +148,7 @@ export class CallableMockInstance {
|
|
|
135
148
|
return this;
|
|
136
149
|
}
|
|
137
150
|
|
|
138
|
-
pipe(plugin:
|
|
151
|
+
pipe(plugin: Plugin): this {
|
|
139
152
|
this.plugins.push(plugin);
|
|
140
153
|
this.logger.log(
|
|
141
154
|
"plugin",
|
|
@@ -151,10 +164,10 @@ export class CallableMockInstance {
|
|
|
151
164
|
}
|
|
152
165
|
|
|
153
166
|
async handle(
|
|
154
|
-
method:
|
|
167
|
+
method: HttpMethod,
|
|
155
168
|
path: string,
|
|
156
|
-
options?:
|
|
157
|
-
): Promise<
|
|
169
|
+
options?: RequestOptions,
|
|
170
|
+
): Promise<Response> {
|
|
158
171
|
const requestId = Math.random().toString(36).substring(7);
|
|
159
172
|
this.logger.log("request", `[${requestId}] ${method} ${path}`, {
|
|
160
173
|
headers: options?.headers,
|
|
@@ -235,7 +248,7 @@ export class CallableMockInstance {
|
|
|
235
248
|
const params = this.extractParams(matchedRoute, requestPath);
|
|
236
249
|
|
|
237
250
|
// Generate initial response from route handler
|
|
238
|
-
const context:
|
|
251
|
+
const context: RequestContext = {
|
|
239
252
|
method,
|
|
240
253
|
path: requestPath,
|
|
241
254
|
params,
|
|
@@ -247,15 +260,13 @@ export class CallableMockInstance {
|
|
|
247
260
|
|
|
248
261
|
let result: any;
|
|
249
262
|
if (typeof matchedRoute.generator === "function") {
|
|
250
|
-
result = await (matchedRoute.generator as
|
|
251
|
-
context,
|
|
252
|
-
);
|
|
263
|
+
result = await (matchedRoute.generator as GeneratorFunction)(context);
|
|
253
264
|
} else {
|
|
254
265
|
result = matchedRoute.generator;
|
|
255
266
|
}
|
|
256
267
|
|
|
257
268
|
// Build plugin context
|
|
258
|
-
let pluginContext:
|
|
269
|
+
let pluginContext: PluginContext = {
|
|
259
270
|
path: requestPath,
|
|
260
271
|
route: matchedRoute.config,
|
|
261
272
|
method,
|
|
@@ -360,10 +371,7 @@ export class CallableMockInstance {
|
|
|
360
371
|
* @returns Normalized Response object with status, body, and headers
|
|
361
372
|
* @private
|
|
362
373
|
*/
|
|
363
|
-
private parseResponse(
|
|
364
|
-
result: any,
|
|
365
|
-
routeConfig: Schmock.RouteConfig,
|
|
366
|
-
): Schmock.Response {
|
|
374
|
+
private parseResponse(result: any, routeConfig: RouteConfig): Response {
|
|
367
375
|
let status = 200;
|
|
368
376
|
let body = result;
|
|
369
377
|
let headers: Record<string, string> = {};
|
|
@@ -433,11 +441,11 @@ export class CallableMockInstance {
|
|
|
433
441
|
* @private
|
|
434
442
|
*/
|
|
435
443
|
private async runPluginPipeline(
|
|
436
|
-
context:
|
|
444
|
+
context: PluginContext,
|
|
437
445
|
initialResponse?: any,
|
|
438
|
-
_routeConfig?:
|
|
446
|
+
_routeConfig?: RouteConfig,
|
|
439
447
|
_requestId?: string,
|
|
440
|
-
): Promise<{ context:
|
|
448
|
+
): Promise<{ context: PluginContext; response?: any }> {
|
|
441
449
|
let currentContext = context;
|
|
442
450
|
let response: any = initialResponse;
|
|
443
451
|
|
|
@@ -525,7 +533,7 @@ export class CallableMockInstance {
|
|
|
525
533
|
* @private
|
|
526
534
|
*/
|
|
527
535
|
private findRoute(
|
|
528
|
-
method:
|
|
536
|
+
method: HttpMethod,
|
|
529
537
|
path: string,
|
|
530
538
|
): CompiledCallableRoute | undefined {
|
|
531
539
|
// First pass: Look for exact matches (routes without parameters)
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
import { CallableMockInstance } from "./builder";
|
|
1
|
+
import { CallableMockInstance as CallableMockInstanceImpl } from "./builder.js";
|
|
2
|
+
import type {
|
|
3
|
+
CallableMockInstance,
|
|
4
|
+
Generator,
|
|
5
|
+
GlobalConfig,
|
|
6
|
+
Plugin,
|
|
7
|
+
RouteConfig,
|
|
8
|
+
RouteKey,
|
|
9
|
+
} from "./types.js";
|
|
2
10
|
|
|
3
11
|
/**
|
|
4
12
|
* Create a new Schmock mock instance with callable API.
|
|
@@ -23,30 +31,28 @@ import { CallableMockInstance } from "./builder";
|
|
|
23
31
|
* @param config Optional global configuration
|
|
24
32
|
* @returns A callable mock instance
|
|
25
33
|
*/
|
|
26
|
-
export function schmock(
|
|
27
|
-
config?: Schmock.GlobalConfig,
|
|
28
|
-
): Schmock.CallableMockInstance {
|
|
34
|
+
export function schmock(config?: GlobalConfig): CallableMockInstance {
|
|
29
35
|
// Always use new callable API
|
|
30
|
-
const instance = new
|
|
36
|
+
const instance = new CallableMockInstanceImpl(config || {});
|
|
31
37
|
|
|
32
38
|
// Create a callable function that wraps the instance
|
|
33
39
|
const callableInstance = ((
|
|
34
|
-
route:
|
|
35
|
-
generator:
|
|
36
|
-
|
|
40
|
+
route: RouteKey,
|
|
41
|
+
generator: Generator,
|
|
42
|
+
routeConfig: RouteConfig = {},
|
|
37
43
|
) => {
|
|
38
|
-
instance.defineRoute(route, generator,
|
|
44
|
+
instance.defineRoute(route, generator, routeConfig);
|
|
39
45
|
return callableInstance; // Return the callable function for chaining
|
|
40
46
|
}) as any;
|
|
41
47
|
|
|
42
48
|
// Manually bind all instance methods to the callable function with proper return values
|
|
43
|
-
callableInstance.pipe = (plugin:
|
|
49
|
+
callableInstance.pipe = (plugin: Plugin) => {
|
|
44
50
|
instance.pipe(plugin);
|
|
45
51
|
return callableInstance; // Return callable function for chaining
|
|
46
52
|
};
|
|
47
53
|
callableInstance.handle = instance.handle.bind(instance);
|
|
48
54
|
|
|
49
|
-
return callableInstance as
|
|
55
|
+
return callableInstance as CallableMockInstance;
|
|
50
56
|
}
|
|
51
57
|
|
|
52
58
|
// Re-export errors
|
|
@@ -60,7 +66,8 @@ export {
|
|
|
60
66
|
SchemaGenerationError,
|
|
61
67
|
SchemaValidationError,
|
|
62
68
|
SchmockError,
|
|
63
|
-
} from "./errors";
|
|
69
|
+
} from "./errors.js";
|
|
70
|
+
|
|
64
71
|
// Re-export types
|
|
65
72
|
export type {
|
|
66
73
|
CallableMockInstance,
|
|
@@ -77,4 +84,4 @@ export type {
|
|
|
77
84
|
ResponseResult,
|
|
78
85
|
RouteConfig,
|
|
79
86
|
RouteKey,
|
|
80
|
-
} from "./types";
|
|
87
|
+
} from "./types.js";
|
package/src/parser.ts
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
|
-
import { RouteParseError } from "./errors";
|
|
2
|
-
import type { HttpMethod } from "./types";
|
|
1
|
+
import { RouteParseError } from "./errors.js";
|
|
2
|
+
import type { HttpMethod } from "./types.js";
|
|
3
|
+
|
|
4
|
+
const HTTP_METHODS: readonly HttpMethod[] = [
|
|
5
|
+
"GET",
|
|
6
|
+
"POST",
|
|
7
|
+
"PUT",
|
|
8
|
+
"DELETE",
|
|
9
|
+
"PATCH",
|
|
10
|
+
"HEAD",
|
|
11
|
+
"OPTIONS",
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
function isHttpMethod(method: string): method is HttpMethod {
|
|
15
|
+
return HTTP_METHODS.includes(method as HttpMethod);
|
|
16
|
+
}
|
|
3
17
|
|
|
4
18
|
export interface ParsedRoute {
|
|
5
19
|
method: HttpMethod;
|
|
@@ -52,8 +66,13 @@ export function parseRouteKey(routeKey: string): ParsedRoute {
|
|
|
52
66
|
|
|
53
67
|
const pattern = new RegExp(`^${regexPath}$`);
|
|
54
68
|
|
|
69
|
+
// The regex guarantees method is valid, but we use the type guard for type safety
|
|
70
|
+
if (!isHttpMethod(method)) {
|
|
71
|
+
throw new RouteParseError(routeKey, `Invalid HTTP method: ${method}`);
|
|
72
|
+
}
|
|
73
|
+
|
|
55
74
|
return {
|
|
56
|
-
method
|
|
75
|
+
method,
|
|
57
76
|
path,
|
|
58
77
|
pattern,
|
|
59
78
|
params,
|
package/src/types.ts
CHANGED
|
@@ -1,17 +1,259 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
export
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
1
|
+
/**
|
|
2
|
+
* JSON Schema type (simplified for core package)
|
|
3
|
+
* Full schema support available via @schmock/schema
|
|
4
|
+
*/
|
|
5
|
+
export interface JSONSchema {
|
|
6
|
+
type?: string | string[];
|
|
7
|
+
properties?: Record<string, JSONSchema>;
|
|
8
|
+
items?: JSONSchema | JSONSchema[];
|
|
9
|
+
required?: string[];
|
|
10
|
+
enum?: any[];
|
|
11
|
+
const?: any;
|
|
12
|
+
[key: string]: any;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* HTTP methods supported by Schmock
|
|
17
|
+
*/
|
|
18
|
+
export type HttpMethod =
|
|
19
|
+
| "GET"
|
|
20
|
+
| "POST"
|
|
21
|
+
| "PUT"
|
|
22
|
+
| "DELETE"
|
|
23
|
+
| "PATCH"
|
|
24
|
+
| "HEAD"
|
|
25
|
+
| "OPTIONS";
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Route key format: 'METHOD /path'
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* 'GET /users'
|
|
32
|
+
* 'POST /users/:id'
|
|
33
|
+
* 'DELETE /api/posts/:postId/comments/:commentId'
|
|
34
|
+
*/
|
|
35
|
+
export type RouteKey = `${HttpMethod} ${string}`;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Plugin interface for extending Schmock functionality
|
|
39
|
+
*/
|
|
40
|
+
export interface Plugin {
|
|
41
|
+
/** Unique plugin identifier */
|
|
42
|
+
name: string;
|
|
43
|
+
/** Plugin version (semver) */
|
|
44
|
+
version?: string;
|
|
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(
|
|
54
|
+
context: PluginContext,
|
|
55
|
+
response?: any,
|
|
56
|
+
): PluginResult | Promise<PluginResult>;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Called when an error occurs
|
|
60
|
+
* Can handle, transform, or suppress errors
|
|
61
|
+
* @param error - The error that occurred
|
|
62
|
+
* @param context - Plugin context
|
|
63
|
+
* @returns Modified error, response data, or void to continue error propagation
|
|
64
|
+
*/
|
|
65
|
+
onError?(
|
|
66
|
+
error: Error,
|
|
67
|
+
context: PluginContext,
|
|
68
|
+
):
|
|
69
|
+
| Error
|
|
70
|
+
| ResponseResult
|
|
71
|
+
| undefined
|
|
72
|
+
| Promise<Error | ResponseResult | undefined>;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Result returned by plugin process method
|
|
77
|
+
*/
|
|
78
|
+
export interface PluginResult {
|
|
79
|
+
/** Updated context */
|
|
80
|
+
context: PluginContext;
|
|
81
|
+
/** Response data (if generated/modified) */
|
|
82
|
+
response?: any;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Context passed through plugin pipeline
|
|
87
|
+
*/
|
|
88
|
+
export interface PluginContext {
|
|
89
|
+
/** Request path */
|
|
90
|
+
path: string;
|
|
91
|
+
/** Matched route configuration */
|
|
92
|
+
route: any;
|
|
93
|
+
/** HTTP method */
|
|
94
|
+
method: HttpMethod;
|
|
95
|
+
/** Route parameters */
|
|
96
|
+
params: Record<string, string>;
|
|
97
|
+
/** Query parameters */
|
|
98
|
+
query: Record<string, string>;
|
|
99
|
+
/** Request headers */
|
|
100
|
+
headers: Record<string, string>;
|
|
101
|
+
/** Request body */
|
|
102
|
+
body?: any;
|
|
103
|
+
/** Shared state between plugins for this request */
|
|
104
|
+
state: Map<string, any>;
|
|
105
|
+
/** Route-specific state */
|
|
106
|
+
routeState?: any;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Global configuration options for the mock instance
|
|
111
|
+
*/
|
|
112
|
+
export interface GlobalConfig {
|
|
113
|
+
/** Base path prefix for all routes */
|
|
114
|
+
namespace?: string;
|
|
115
|
+
/** Response delay in ms, or [min, max] for random delay */
|
|
116
|
+
delay?: number | [number, number];
|
|
117
|
+
/** Enable debug mode for detailed logging */
|
|
118
|
+
debug?: boolean;
|
|
119
|
+
/** Initial shared state object */
|
|
120
|
+
state?: any;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Route-specific configuration options
|
|
125
|
+
*/
|
|
126
|
+
export interface RouteConfig {
|
|
127
|
+
/** MIME type for content type validation (auto-detected if not provided) */
|
|
128
|
+
contentType?: string;
|
|
129
|
+
/** Additional route-specific options */
|
|
130
|
+
[key: string]: any;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Generator types that can be passed to route definitions
|
|
135
|
+
*/
|
|
136
|
+
export type Generator = GeneratorFunction | StaticData | JSONSchema;
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Function that generates responses
|
|
140
|
+
*/
|
|
141
|
+
export type GeneratorFunction = (
|
|
142
|
+
context: RequestContext,
|
|
143
|
+
) => ResponseResult | Promise<ResponseResult>;
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Static data (non-function) that gets returned as-is
|
|
147
|
+
*/
|
|
148
|
+
export type StaticData = any;
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Context passed to generator functions
|
|
152
|
+
*/
|
|
153
|
+
export interface RequestContext {
|
|
154
|
+
/** HTTP method */
|
|
155
|
+
method: HttpMethod;
|
|
156
|
+
/** Request path */
|
|
157
|
+
path: string;
|
|
158
|
+
/** Route parameters (e.g., :id) */
|
|
159
|
+
params: Record<string, string>;
|
|
160
|
+
/** Query string parameters */
|
|
161
|
+
query: Record<string, string>;
|
|
162
|
+
/** Request headers */
|
|
163
|
+
headers: Record<string, string>;
|
|
164
|
+
/** Request body (for POST, PUT, PATCH) */
|
|
165
|
+
body?: any;
|
|
166
|
+
/** Shared mutable state */
|
|
167
|
+
state: any;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Response result types:
|
|
172
|
+
* - Any value: returns as 200 OK
|
|
173
|
+
* - [status, body]: custom status with body
|
|
174
|
+
* - [status, body, headers]: custom status, body, and headers
|
|
175
|
+
*/
|
|
176
|
+
export type ResponseResult =
|
|
177
|
+
| any
|
|
178
|
+
| [number, any]
|
|
179
|
+
| [number, any, Record<string, string>];
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Response object returned by handle method
|
|
183
|
+
*/
|
|
184
|
+
export interface Response {
|
|
185
|
+
status: number;
|
|
186
|
+
body: any;
|
|
187
|
+
headers: Record<string, string>;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Options for handle method
|
|
192
|
+
*/
|
|
193
|
+
export interface RequestOptions {
|
|
194
|
+
headers?: Record<string, string>;
|
|
195
|
+
body?: any;
|
|
196
|
+
query?: Record<string, string>;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Main callable mock instance interface
|
|
201
|
+
*/
|
|
202
|
+
export interface CallableMockInstance {
|
|
203
|
+
/**
|
|
204
|
+
* Define a route by calling the instance directly
|
|
205
|
+
*
|
|
206
|
+
* @param route - Route pattern in format 'METHOD /path'
|
|
207
|
+
* @param generator - Response generator (function, static data, or schema)
|
|
208
|
+
* @param config - Route-specific configuration
|
|
209
|
+
* @returns The same instance for method chaining
|
|
210
|
+
*
|
|
211
|
+
* @example
|
|
212
|
+
* ```typescript
|
|
213
|
+
* const mock = schmock()
|
|
214
|
+
* mock('GET /users', () => [...users], { contentType: 'application/json' })
|
|
215
|
+
* mock('POST /users', userData, { contentType: 'application/json' })
|
|
216
|
+
* ```
|
|
217
|
+
*/
|
|
218
|
+
(
|
|
219
|
+
route: RouteKey,
|
|
220
|
+
generator: Generator,
|
|
221
|
+
config?: RouteConfig,
|
|
222
|
+
): CallableMockInstance;
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Add a plugin to the pipeline
|
|
226
|
+
*
|
|
227
|
+
* @param plugin - Plugin to add to the pipeline
|
|
228
|
+
* @returns The same instance for method chaining
|
|
229
|
+
*
|
|
230
|
+
* @example
|
|
231
|
+
* ```typescript
|
|
232
|
+
* mock('GET /users', generator, config)
|
|
233
|
+
* .pipe(authPlugin())
|
|
234
|
+
* .pipe(corsPlugin())
|
|
235
|
+
* ```
|
|
236
|
+
*/
|
|
237
|
+
pipe(plugin: Plugin): CallableMockInstance;
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Handle a request and return a response
|
|
241
|
+
*
|
|
242
|
+
* @param method - HTTP method
|
|
243
|
+
* @param path - Request path
|
|
244
|
+
* @param options - Request options (headers, body, query)
|
|
245
|
+
* @returns Promise resolving to response object
|
|
246
|
+
*
|
|
247
|
+
* @example
|
|
248
|
+
* ```typescript
|
|
249
|
+
* const response = await mock.handle('GET', '/users', {
|
|
250
|
+
* headers: { 'Authorization': 'Bearer token' }
|
|
251
|
+
* })
|
|
252
|
+
* ```
|
|
253
|
+
*/
|
|
254
|
+
handle(
|
|
255
|
+
method: HttpMethod,
|
|
256
|
+
path: string,
|
|
257
|
+
options?: RequestOptions,
|
|
258
|
+
): Promise<Response>;
|
|
259
|
+
}
|