@schmock/core 1.9.0 → 1.9.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 +0 -40
- package/dist/builder.d.ts.map +1 -1
- package/dist/builder.js +65 -217
- package/dist/constants.d.ts +2 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +7 -0
- package/dist/errors.d.ts +1 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +3 -0
- package/dist/http-helpers.d.ts +4 -1
- package/dist/http-helpers.d.ts.map +1 -1
- package/dist/http-helpers.js +18 -3
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/plugin-pipeline.d.ts +15 -0
- package/dist/plugin-pipeline.d.ts.map +1 -0
- package/dist/plugin-pipeline.js +66 -0
- package/dist/response-parser.d.ts +6 -0
- package/dist/response-parser.d.ts.map +1 -0
- package/dist/response-parser.js +57 -0
- package/dist/route-matcher.d.ts +24 -0
- package/dist/route-matcher.d.ts.map +1 -0
- package/dist/route-matcher.js +39 -0
- package/package.json +3 -2
- package/src/builder.ts +83 -314
- package/src/constants.ts +9 -0
- package/src/errors.ts +4 -0
- package/src/http-helpers.ts +24 -2
- package/src/index.ts +1 -0
- package/src/plugin-pipeline.ts +100 -0
- package/src/response-parser.ts +74 -0
- package/src/route-matcher.ts +69 -0
package/dist/builder.d.ts
CHANGED
|
@@ -39,45 +39,5 @@ export declare class CallableMockInstance {
|
|
|
39
39
|
* @private
|
|
40
40
|
*/
|
|
41
41
|
private applyDelay;
|
|
42
|
-
/**
|
|
43
|
-
* Parse and normalize response result into Response object
|
|
44
|
-
* Handles tuple format [status, body, headers], direct values, and response objects
|
|
45
|
-
* @param result - Raw result from generator or plugin
|
|
46
|
-
* @param routeConfig - Route configuration for content-type defaults
|
|
47
|
-
* @returns Normalized Response object with status, body, and headers
|
|
48
|
-
* @private
|
|
49
|
-
*/
|
|
50
|
-
private parseResponse;
|
|
51
|
-
/**
|
|
52
|
-
* Run all registered plugins in sequence
|
|
53
|
-
* First plugin to set response becomes generator, subsequent plugins transform
|
|
54
|
-
* Handles plugin errors via onError hooks
|
|
55
|
-
* @param context - Plugin context with request details
|
|
56
|
-
* @param initialResponse - Initial response from route generator
|
|
57
|
-
* @param _routeConfig - Route config (unused but kept for signature)
|
|
58
|
-
* @param _requestId - Request ID (unused but kept for signature)
|
|
59
|
-
* @returns Updated context and final response after all plugins
|
|
60
|
-
* @private
|
|
61
|
-
*/
|
|
62
|
-
private runPluginPipeline;
|
|
63
|
-
/**
|
|
64
|
-
* Find a route that matches the given method and path
|
|
65
|
-
* Uses two-pass matching: static routes first, then parameterized routes
|
|
66
|
-
* Matches routes in registration order (first registered wins)
|
|
67
|
-
* @param method - HTTP method to match
|
|
68
|
-
* @param path - Request path to match
|
|
69
|
-
* @returns Matched compiled route or undefined if no match
|
|
70
|
-
* @private
|
|
71
|
-
*/
|
|
72
|
-
private findRoute;
|
|
73
|
-
/**
|
|
74
|
-
* Extract parameter values from path based on route pattern
|
|
75
|
-
* Maps capture groups from regex match to parameter names
|
|
76
|
-
* @param route - Compiled route with pattern and param names
|
|
77
|
-
* @param path - Request path to extract values from
|
|
78
|
-
* @returns Object mapping parameter names to extracted values
|
|
79
|
-
* @private
|
|
80
|
-
*/
|
|
81
|
-
private extractParams;
|
|
82
42
|
}
|
|
83
43
|
//# sourceMappingURL=builder.d.ts.map
|
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":"AAuDA;;;;GAIG;AACH,qBAAa,oBAAoB;IAYnB,OAAO,CAAC,YAAY;IAXhC,OAAO,CAAC,MAAM,CAA+B;IAC7C,OAAO,CAAC,YAAY,CAA4C;IAChE,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,cAAc,CAA+B;IACrD,OAAO,CAAC,WAAW,CAA2C;IAC9D,OAAO,CAAC,MAAM,CAAqB;IACnC,OAAO,CAAC,UAAU,CAAiC;IAEnD,OAAO,CAAC,SAAS,CAAoC;gBAEjC,YAAY,GAAE,OAAO,CAAC,YAAiB;IAa3D,WAAW,CACT,KAAK,EAAE,OAAO,CAAC,QAAQ,EACvB,SAAS,EAAE,OAAO,CAAC,SAAS,EAC5B,MAAM,EAAE,OAAO,CAAC,WAAW,GAC1B,IAAI;IAiFP,cAAc,CAAC,GAAG,EAAE,OAAO,CAAC,oBAAoB,GAAG,IAAI;IAIvD,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,GAAG,IAAI;IAoBlC,OAAO,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,UAAU,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE;IAS5E,MAAM,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,UAAU,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO;IAS3D,SAAS,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,UAAU,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM;IAS7D,WAAW,CACT,MAAM,CAAC,EAAE,OAAO,CAAC,UAAU,EAC3B,IAAI,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,aAAa,GAAG,SAAS;IAYpC,SAAS,IAAI,OAAO,CAAC,SAAS,EAAE;IAQhC,QAAQ,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAMnC,EAAE,CAAC,CAAC,SAAS,OAAO,CAAC,YAAY,EAC/B,KAAK,EAAE,CAAC,EACR,QAAQ,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC,KAAK,IAAI,GACnD,IAAI;IAUP,GAAG,CAAC,CAAC,SAAS,OAAO,CAAC,YAAY,EAChC,KAAK,EAAE,CAAC,EACR,QAAQ,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC,KAAK,IAAI,GACnD,IAAI;IAKP,OAAO,CAAC,IAAI;IAWZ,KAAK,IAAI,IAAI;IAeb,YAAY,IAAI,IAAI;IAKpB,UAAU,IAAI,IAAI;IAWlB,MAAM,CAAC,IAAI,SAAI,EAAE,QAAQ,SAAc,GAAG,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC;IAqDrE,KAAK,IAAI,IAAI;IAUP,MAAM,CACV,MAAM,EAAE,OAAO,CAAC,UAAU,EAC1B,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,OAAO,CAAC,cAAc,GAC/B,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC;IAoO5B;;;;OAIG;YACW,UAAU;CAezB"}
|
package/dist/builder.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { createServer } from "node:http";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { normalizePath, toHttpMethod } from "./constants.js";
|
|
3
|
+
import { errorMessage, RouteDefinitionError, RouteNotFoundError, SchmockError, } from "./errors.js";
|
|
4
4
|
import { collectBody, parseNodeHeaders, parseNodeQuery, writeSchmockResponse, } from "./http-helpers.js";
|
|
5
5
|
import { parseRouteKey } from "./parser.js";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
}
|
|
6
|
+
import { runPluginPipeline } from "./plugin-pipeline.js";
|
|
7
|
+
import { parseResponse } from "./response-parser.js";
|
|
8
|
+
import { extractParams, findRoute, isGeneratorFunction, } from "./route-matcher.js";
|
|
9
9
|
/**
|
|
10
10
|
* Debug logger that respects debug mode configuration
|
|
11
11
|
*/
|
|
@@ -37,15 +37,6 @@ class DebugLogger {
|
|
|
37
37
|
console.timeEnd(`[SCHMOCK] ${label}`);
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
|
-
function isGeneratorFunction(gen) {
|
|
41
|
-
return typeof gen === "function";
|
|
42
|
-
}
|
|
43
|
-
function isResponseObject(value) {
|
|
44
|
-
return (typeof value === "object" &&
|
|
45
|
-
value !== null &&
|
|
46
|
-
"status" in value &&
|
|
47
|
-
"body" in value);
|
|
48
|
-
}
|
|
49
40
|
/**
|
|
50
41
|
* Callable mock instance that implements the new API.
|
|
51
42
|
*
|
|
@@ -128,10 +119,7 @@ export class CallableMockInstance {
|
|
|
128
119
|
// Store static routes (no params) in Map for O(1) lookup
|
|
129
120
|
// Only store the first registration — "first registration wins" semantics
|
|
130
121
|
if (parsed.params.length === 0) {
|
|
131
|
-
const
|
|
132
|
-
? parsed.path.slice(0, -1)
|
|
133
|
-
: parsed.path;
|
|
134
|
-
const key = `${parsed.method} ${normalizedPath}`;
|
|
122
|
+
const key = `${parsed.method} ${normalizePath(parsed.path)}`;
|
|
135
123
|
if (!this.staticRoutes.has(key)) {
|
|
136
124
|
this.staticRoutes.set(key, compiledRoute);
|
|
137
125
|
}
|
|
@@ -161,26 +149,26 @@ export class CallableMockInstance {
|
|
|
161
149
|
}
|
|
162
150
|
// ===== Request Spy / History API =====
|
|
163
151
|
history(method, path) {
|
|
164
|
-
if (method
|
|
165
|
-
return this.requestHistory.filter((r) => r.method === method && r.path === path);
|
|
152
|
+
if (method || path) {
|
|
153
|
+
return this.requestHistory.filter((r) => (!method || r.method === method) && (!path || r.path === path));
|
|
166
154
|
}
|
|
167
155
|
return [...this.requestHistory];
|
|
168
156
|
}
|
|
169
157
|
called(method, path) {
|
|
170
|
-
if (method
|
|
171
|
-
return this.requestHistory.some((r) => r.method === method && r.path === path);
|
|
158
|
+
if (method || path) {
|
|
159
|
+
return this.requestHistory.some((r) => (!method || r.method === method) && (!path || r.path === path));
|
|
172
160
|
}
|
|
173
161
|
return this.requestHistory.length > 0;
|
|
174
162
|
}
|
|
175
163
|
callCount(method, path) {
|
|
176
|
-
if (method
|
|
177
|
-
return this.requestHistory.filter((r) => r.method === method && r.path === path).length;
|
|
164
|
+
if (method || path) {
|
|
165
|
+
return this.requestHistory.filter((r) => (!method || r.method === method) && (!path || r.path === path)).length;
|
|
178
166
|
}
|
|
179
167
|
return this.requestHistory.length;
|
|
180
168
|
}
|
|
181
169
|
lastRequest(method, path) {
|
|
182
|
-
if (method
|
|
183
|
-
const filtered = this.requestHistory.filter((r) => r.method === method && r.path === path);
|
|
170
|
+
if (method || path) {
|
|
171
|
+
const filtered = this.requestHistory.filter((r) => (!method || r.method === method) && (!path || r.path === path));
|
|
184
172
|
return filtered[filtered.length - 1];
|
|
185
173
|
}
|
|
186
174
|
return this.requestHistory[this.requestHistory.length - 1];
|
|
@@ -194,7 +182,7 @@ export class CallableMockInstance {
|
|
|
194
182
|
}));
|
|
195
183
|
}
|
|
196
184
|
getState() {
|
|
197
|
-
return this.globalConfig.state || {};
|
|
185
|
+
return { ...(this.globalConfig.state || {}) };
|
|
198
186
|
}
|
|
199
187
|
// ===== Lifecycle Events =====
|
|
200
188
|
on(event, listener) {
|
|
@@ -251,14 +239,29 @@ export class CallableMockInstance {
|
|
|
251
239
|
throw new SchmockError("Server is already running", "SERVER_ALREADY_RUNNING");
|
|
252
240
|
}
|
|
253
241
|
const httpServer = createServer((req, res) => {
|
|
254
|
-
const
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
242
|
+
const handleRequest = async () => {
|
|
243
|
+
const url = new URL(req.url ?? "/", `http://${req.headers.host}`);
|
|
244
|
+
const method = toHttpMethod(req.method ?? "GET");
|
|
245
|
+
const path = url.pathname;
|
|
246
|
+
const headers = parseNodeHeaders(req);
|
|
247
|
+
const query = parseNodeQuery(url);
|
|
248
|
+
const body = await collectBody(req, headers);
|
|
249
|
+
const schmockResponse = await this.handle(method, path, {
|
|
250
|
+
headers,
|
|
251
|
+
body,
|
|
252
|
+
query,
|
|
253
|
+
});
|
|
260
254
|
writeSchmockResponse(res, schmockResponse);
|
|
261
|
-
}
|
|
255
|
+
};
|
|
256
|
+
handleRequest().catch((error) => {
|
|
257
|
+
if (!res.headersSent) {
|
|
258
|
+
res.writeHead(500, { "content-type": "application/json" });
|
|
259
|
+
}
|
|
260
|
+
res.end(JSON.stringify({
|
|
261
|
+
error: error instanceof Error ? error.message : "Internal Server Error",
|
|
262
|
+
code: "SERVER_ERROR",
|
|
263
|
+
}));
|
|
264
|
+
});
|
|
262
265
|
});
|
|
263
266
|
this.server = httpServer;
|
|
264
267
|
return new Promise((resolve, reject) => {
|
|
@@ -282,18 +285,20 @@ export class CallableMockInstance {
|
|
|
282
285
|
this.logger.log("server", "Server stopped");
|
|
283
286
|
}
|
|
284
287
|
async handle(method, path, options) {
|
|
285
|
-
const requestId = crypto.randomUUID();
|
|
286
288
|
const handleStart = performance.now();
|
|
289
|
+
const requestId = this.globalConfig.debug ? crypto.randomUUID() : "";
|
|
290
|
+
const reqQuery = options?.query || {};
|
|
291
|
+
const reqHeaders = options?.headers || {};
|
|
287
292
|
this.logger.log("request", `[${requestId}] ${method} ${path}`, {
|
|
288
|
-
headers:
|
|
289
|
-
query:
|
|
293
|
+
headers: reqHeaders,
|
|
294
|
+
query: reqQuery,
|
|
290
295
|
bodyType: options?.body ? typeof options.body : "none",
|
|
291
296
|
});
|
|
292
297
|
this.logger.time(`request-${requestId}`);
|
|
293
298
|
this.emit("request:start", {
|
|
294
299
|
method,
|
|
295
300
|
path,
|
|
296
|
-
headers:
|
|
301
|
+
headers: reqHeaders,
|
|
297
302
|
});
|
|
298
303
|
try {
|
|
299
304
|
// Apply namespace if configured
|
|
@@ -316,6 +321,12 @@ export class CallableMockInstance {
|
|
|
316
321
|
body: { error: error.message, code: error.code },
|
|
317
322
|
headers: {},
|
|
318
323
|
};
|
|
324
|
+
this.emit("request:end", {
|
|
325
|
+
method,
|
|
326
|
+
path,
|
|
327
|
+
status: 404,
|
|
328
|
+
duration: performance.now() - handleStart,
|
|
329
|
+
});
|
|
319
330
|
this.logger.timeEnd(`request-${requestId}`);
|
|
320
331
|
return response;
|
|
321
332
|
}
|
|
@@ -324,7 +335,7 @@ export class CallableMockInstance {
|
|
|
324
335
|
requestPath = stripped.startsWith("/") ? stripped : `/${stripped}`;
|
|
325
336
|
}
|
|
326
337
|
// Find matching route
|
|
327
|
-
const matchedRoute =
|
|
338
|
+
const matchedRoute = findRoute(method, requestPath, this.staticRoutes, this.routes);
|
|
328
339
|
if (!matchedRoute) {
|
|
329
340
|
this.logger.log("route", `[${requestId}] No route found for ${method} ${requestPath}`);
|
|
330
341
|
this.emit("request:notfound", { method, path: requestPath });
|
|
@@ -345,7 +356,7 @@ export class CallableMockInstance {
|
|
|
345
356
|
}
|
|
346
357
|
this.logger.log("route", `[${requestId}] Matched route: ${method} ${matchedRoute.path}`);
|
|
347
358
|
// Extract parameters from the matched route
|
|
348
|
-
const params =
|
|
359
|
+
const params = extractParams(matchedRoute, requestPath);
|
|
349
360
|
this.emit("request:match", {
|
|
350
361
|
method,
|
|
351
362
|
path: requestPath,
|
|
@@ -357,8 +368,8 @@ export class CallableMockInstance {
|
|
|
357
368
|
method,
|
|
358
369
|
path: requestPath,
|
|
359
370
|
params,
|
|
360
|
-
query:
|
|
361
|
-
headers:
|
|
371
|
+
query: reqQuery,
|
|
372
|
+
headers: reqHeaders,
|
|
362
373
|
body: options?.body,
|
|
363
374
|
state: this.globalConfig.state || {},
|
|
364
375
|
};
|
|
@@ -375,15 +386,15 @@ export class CallableMockInstance {
|
|
|
375
386
|
route: matchedRoute.config,
|
|
376
387
|
method,
|
|
377
388
|
params,
|
|
378
|
-
query:
|
|
379
|
-
headers:
|
|
389
|
+
query: reqQuery,
|
|
390
|
+
headers: reqHeaders,
|
|
380
391
|
body: options?.body,
|
|
381
392
|
state: new Map(),
|
|
382
393
|
routeState: this.globalConfig.state || {},
|
|
383
394
|
};
|
|
384
395
|
// Run plugin pipeline to transform the response
|
|
385
396
|
try {
|
|
386
|
-
const pipelineResult = await this.
|
|
397
|
+
const pipelineResult = await runPluginPipeline(this.plugins, pluginContext, result, this.logger);
|
|
387
398
|
pluginContext = pipelineResult.context;
|
|
388
399
|
result = pipelineResult.response;
|
|
389
400
|
}
|
|
@@ -392,7 +403,7 @@ export class CallableMockInstance {
|
|
|
392
403
|
throw error;
|
|
393
404
|
}
|
|
394
405
|
// Parse and prepare response
|
|
395
|
-
const response =
|
|
406
|
+
const response = parseResponse(result, matchedRoute.config);
|
|
396
407
|
// Apply delay (route-level overrides global)
|
|
397
408
|
await this.applyDelay(matchedRoute.config.delay);
|
|
398
409
|
// Record request in history
|
|
@@ -400,8 +411,8 @@ export class CallableMockInstance {
|
|
|
400
411
|
method,
|
|
401
412
|
path: requestPath,
|
|
402
413
|
params,
|
|
403
|
-
query:
|
|
404
|
-
headers:
|
|
414
|
+
query: reqQuery,
|
|
415
|
+
headers: reqHeaders,
|
|
405
416
|
body: options?.body,
|
|
406
417
|
timestamp: Date.now(),
|
|
407
418
|
response: { status: response.status, body: response.body },
|
|
@@ -434,6 +445,12 @@ export class CallableMockInstance {
|
|
|
434
445
|
};
|
|
435
446
|
// Apply delay even for error responses
|
|
436
447
|
await this.applyDelay();
|
|
448
|
+
this.emit("request:end", {
|
|
449
|
+
method,
|
|
450
|
+
path,
|
|
451
|
+
status: 500,
|
|
452
|
+
duration: performance.now() - handleStart,
|
|
453
|
+
});
|
|
437
454
|
this.logger.log("error", `[${requestId}] Returning error response 500`);
|
|
438
455
|
this.logger.timeEnd(`request-${requestId}`);
|
|
439
456
|
return errorResponse;
|
|
@@ -455,173 +472,4 @@ export class CallableMockInstance {
|
|
|
455
472
|
: effectiveDelay;
|
|
456
473
|
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
457
474
|
}
|
|
458
|
-
/**
|
|
459
|
-
* Parse and normalize response result into Response object
|
|
460
|
-
* Handles tuple format [status, body, headers], direct values, and response objects
|
|
461
|
-
* @param result - Raw result from generator or plugin
|
|
462
|
-
* @param routeConfig - Route configuration for content-type defaults
|
|
463
|
-
* @returns Normalized Response object with status, body, and headers
|
|
464
|
-
* @private
|
|
465
|
-
*/
|
|
466
|
-
parseResponse(result, routeConfig) {
|
|
467
|
-
let status = 200;
|
|
468
|
-
let body = result;
|
|
469
|
-
let headers = {};
|
|
470
|
-
let tupleFormat = false;
|
|
471
|
-
// Handle already-formed response objects (from plugin error recovery)
|
|
472
|
-
if (isResponseObject(result)) {
|
|
473
|
-
return {
|
|
474
|
-
status: result.status,
|
|
475
|
-
body: result.body,
|
|
476
|
-
headers: result.headers || {},
|
|
477
|
-
};
|
|
478
|
-
}
|
|
479
|
-
// Handle tuple response format [status, body, headers?]
|
|
480
|
-
if (isStatusTuple(result)) {
|
|
481
|
-
[status, body, headers = {}] = result;
|
|
482
|
-
tupleFormat = true;
|
|
483
|
-
}
|
|
484
|
-
// Handle null/undefined responses with 204 No Content
|
|
485
|
-
// But don't auto-convert if tuple format was used (status was explicitly provided)
|
|
486
|
-
if (body === null || body === undefined) {
|
|
487
|
-
if (!tupleFormat) {
|
|
488
|
-
status = status === 200 ? 204 : status; // Only change to 204 if status wasn't explicitly set via tuple
|
|
489
|
-
}
|
|
490
|
-
body = undefined; // Ensure body is undefined for null responses
|
|
491
|
-
}
|
|
492
|
-
// Add content-type header from route config if it exists and headers don't already have it
|
|
493
|
-
// But only if this isn't a tuple response (where headers are explicitly controlled)
|
|
494
|
-
if (!headers["content-type"] && routeConfig.contentType && !tupleFormat) {
|
|
495
|
-
headers["content-type"] = routeConfig.contentType;
|
|
496
|
-
// Handle special conversion cases when contentType is explicitly set
|
|
497
|
-
if (routeConfig.contentType === "text/plain" && body !== undefined) {
|
|
498
|
-
if (typeof body === "object" && !Buffer.isBuffer(body)) {
|
|
499
|
-
body = JSON.stringify(body);
|
|
500
|
-
}
|
|
501
|
-
else if (typeof body !== "string") {
|
|
502
|
-
body = String(body);
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
return {
|
|
507
|
-
status,
|
|
508
|
-
body,
|
|
509
|
-
headers,
|
|
510
|
-
};
|
|
511
|
-
}
|
|
512
|
-
/**
|
|
513
|
-
* Run all registered plugins in sequence
|
|
514
|
-
* First plugin to set response becomes generator, subsequent plugins transform
|
|
515
|
-
* Handles plugin errors via onError hooks
|
|
516
|
-
* @param context - Plugin context with request details
|
|
517
|
-
* @param initialResponse - Initial response from route generator
|
|
518
|
-
* @param _routeConfig - Route config (unused but kept for signature)
|
|
519
|
-
* @param _requestId - Request ID (unused but kept for signature)
|
|
520
|
-
* @returns Updated context and final response after all plugins
|
|
521
|
-
* @private
|
|
522
|
-
*/
|
|
523
|
-
async runPluginPipeline(context, initialResponse, _routeConfig, _requestId) {
|
|
524
|
-
let currentContext = context;
|
|
525
|
-
let response = initialResponse;
|
|
526
|
-
this.logger.log("pipeline", `Running plugin pipeline for ${this.plugins.length} plugins`);
|
|
527
|
-
for (const plugin of this.plugins) {
|
|
528
|
-
this.logger.log("pipeline", `Processing plugin: ${plugin.name}`);
|
|
529
|
-
try {
|
|
530
|
-
const result = await plugin.process(currentContext, response);
|
|
531
|
-
if (!result || !result.context) {
|
|
532
|
-
throw new Error(`Plugin ${plugin.name} didn't return valid result`);
|
|
533
|
-
}
|
|
534
|
-
currentContext = result.context;
|
|
535
|
-
// First plugin to set response becomes the generator
|
|
536
|
-
if (result.response !== undefined &&
|
|
537
|
-
(response === undefined || response === null)) {
|
|
538
|
-
this.logger.log("pipeline", `Plugin ${plugin.name} generated response`);
|
|
539
|
-
response = result.response;
|
|
540
|
-
}
|
|
541
|
-
else if (result.response !== undefined && response !== undefined) {
|
|
542
|
-
this.logger.log("pipeline", `Plugin ${plugin.name} transformed response`);
|
|
543
|
-
response = result.response;
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
catch (error) {
|
|
547
|
-
this.logger.log("pipeline", `Plugin ${plugin.name} failed: ${errorMessage(error)}`);
|
|
548
|
-
// Try error handling if plugin has onError hook
|
|
549
|
-
if (plugin.onError) {
|
|
550
|
-
try {
|
|
551
|
-
const pluginError = error instanceof Error ? error : new Error(errorMessage(error));
|
|
552
|
-
const errorResult = await plugin.onError(pluginError, currentContext);
|
|
553
|
-
if (errorResult) {
|
|
554
|
-
this.logger.log("pipeline", `Plugin ${plugin.name} handled error`);
|
|
555
|
-
// Error return → transform the thrown error
|
|
556
|
-
if (errorResult instanceof Error) {
|
|
557
|
-
throw new PluginError(plugin.name, errorResult);
|
|
558
|
-
}
|
|
559
|
-
// ResponseResult return → recover, stop pipeline
|
|
560
|
-
if (typeof errorResult === "object" &&
|
|
561
|
-
errorResult !== null &&
|
|
562
|
-
"status" in errorResult) {
|
|
563
|
-
response = errorResult;
|
|
564
|
-
break;
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
// void/falsy return → propagate original error below
|
|
568
|
-
}
|
|
569
|
-
catch (hookError) {
|
|
570
|
-
// If the hook itself threw (including our PluginError above), re-throw it
|
|
571
|
-
if (hookError instanceof PluginError) {
|
|
572
|
-
throw hookError;
|
|
573
|
-
}
|
|
574
|
-
this.logger.log("pipeline", `Plugin ${plugin.name} error handler failed: ${errorMessage(hookError)}`);
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
const cause = error instanceof Error ? error : new Error(errorMessage(error));
|
|
578
|
-
throw new PluginError(plugin.name, cause);
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
return { context: currentContext, response };
|
|
582
|
-
}
|
|
583
|
-
/**
|
|
584
|
-
* Find a route that matches the given method and path
|
|
585
|
-
* Uses two-pass matching: static routes first, then parameterized routes
|
|
586
|
-
* Matches routes in registration order (first registered wins)
|
|
587
|
-
* @param method - HTTP method to match
|
|
588
|
-
* @param path - Request path to match
|
|
589
|
-
* @returns Matched compiled route or undefined if no match
|
|
590
|
-
* @private
|
|
591
|
-
*/
|
|
592
|
-
findRoute(method, path) {
|
|
593
|
-
// O(1) lookup for static routes
|
|
594
|
-
const normalizedPath = path.endsWith("/") && path !== "/" ? path.slice(0, -1) : path;
|
|
595
|
-
const staticMatch = this.staticRoutes.get(`${method} ${normalizedPath}`);
|
|
596
|
-
if (staticMatch) {
|
|
597
|
-
return staticMatch;
|
|
598
|
-
}
|
|
599
|
-
// Fall through to parameterized route scan
|
|
600
|
-
for (const route of this.routes) {
|
|
601
|
-
if (route.method === method &&
|
|
602
|
-
route.params.length > 0 &&
|
|
603
|
-
route.pattern.test(path)) {
|
|
604
|
-
return route;
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
return undefined;
|
|
608
|
-
}
|
|
609
|
-
/**
|
|
610
|
-
* Extract parameter values from path based on route pattern
|
|
611
|
-
* Maps capture groups from regex match to parameter names
|
|
612
|
-
* @param route - Compiled route with pattern and param names
|
|
613
|
-
* @param path - Request path to extract values from
|
|
614
|
-
* @returns Object mapping parameter names to extracted values
|
|
615
|
-
* @private
|
|
616
|
-
*/
|
|
617
|
-
extractParams(route, path) {
|
|
618
|
-
const match = path.match(route.pattern);
|
|
619
|
-
if (!match)
|
|
620
|
-
return {};
|
|
621
|
-
const params = {};
|
|
622
|
-
route.params.forEach((param, index) => {
|
|
623
|
-
params[param] = match[index + 1];
|
|
624
|
-
});
|
|
625
|
-
return params;
|
|
626
|
-
}
|
|
627
475
|
}
|
package/dist/constants.d.ts
CHANGED
|
@@ -3,6 +3,8 @@ export declare const ROUTE_NOT_FOUND_CODE: "ROUTE_NOT_FOUND";
|
|
|
3
3
|
export declare const HTTP_METHODS: readonly HttpMethod[];
|
|
4
4
|
export declare function isHttpMethod(method: string): method is HttpMethod;
|
|
5
5
|
export declare function toHttpMethod(method: string): HttpMethod;
|
|
6
|
+
export declare function normalizePath(path: string): string;
|
|
7
|
+
export declare function toRouteKey(method: HttpMethod, path: string): Schmock.RouteKey;
|
|
6
8
|
/**
|
|
7
9
|
* Check if a value is a status tuple: [status, body] or [status, body, headers]
|
|
8
10
|
* Guards against misinterpreting numeric arrays like [1, 2, 3] as tuples.
|
package/dist/constants.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,eAAO,MAAM,oBAAoB,EAAG,iBAA0B,CAAC;AAE/D,eAAO,MAAM,YAAY,EAAE,SAAS,UAAU,EAQpC,CAAC;AAEX,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,IAAI,UAAU,CAEjE;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAMvD;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,OAAO,GACb,KAAK,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAQxE"}
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,eAAO,MAAM,oBAAoB,EAAG,iBAA0B,CAAC;AAE/D,eAAO,MAAM,YAAY,EAAE,SAAS,UAAU,EAQpC,CAAC;AAEX,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,IAAI,UAAU,CAEjE;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAMvD;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAElD;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAG7E;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,OAAO,GACb,KAAK,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAQxE"}
|
package/dist/constants.js
CHANGED
|
@@ -18,6 +18,13 @@ export function toHttpMethod(method) {
|
|
|
18
18
|
}
|
|
19
19
|
return upper;
|
|
20
20
|
}
|
|
21
|
+
export function normalizePath(path) {
|
|
22
|
+
return path.endsWith("/") && path !== "/" ? path.slice(0, -1) : path;
|
|
23
|
+
}
|
|
24
|
+
export function toRouteKey(method, path) {
|
|
25
|
+
const key = `${method} ${path}`;
|
|
26
|
+
return key;
|
|
27
|
+
}
|
|
21
28
|
/**
|
|
22
29
|
* Check if a value is a status tuple: [status, body] or [status, body, headers]
|
|
23
30
|
* Guards against misinterpreting numeric arrays like [1, 2, 3] as tuples.
|
package/dist/errors.d.ts
CHANGED
package/dist/errors.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,qBAAa,YAAa,SAAQ,KAAK;aAGnB,IAAI,EAAE,MAAM;aACZ,OAAO,CAAC,EAAE,OAAO;gBAFjC,OAAO,EAAE,MAAM,EACC,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,OAAO,YAAA;CAQpC;AAED;;GAEG;AACH,qBAAa,kBAAmB,SAAQ,YAAY;gBACtC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;CAOzC;AAED;;GAEG;AACH,qBAAa,eAAgB,SAAQ,YAAY;gBACnC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CAQ7C;AAED;;GAEG;AACH,qBAAa,uBAAwB,SAAQ,YAAY;gBAC3C,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK;CAQxC;AAED;;GAEG;AACH,qBAAa,WAAY,SAAQ,YAAY;gBAC/B,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK;CAO7C;AAED;;GAEG;AACH,qBAAa,oBAAqB,SAAQ,YAAY;gBACxC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CAQ7C;AAED;;GAEG;AACH,qBAAa,qBAAsB,SAAQ,YAAY;gBACzC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM;CAQnE;AAED;;GAEG;AACH,qBAAa,qBAAsB,SAAQ,YAAY;gBACzC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,EAAE,OAAO;CAQ1D;AAED;;GAEG;AACH,qBAAa,kBAAmB,SAAQ,YAAY;gBACtC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM;CAQ7D"}
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAEnD;AAED;;GAEG;AACH,qBAAa,YAAa,SAAQ,KAAK;aAGnB,IAAI,EAAE,MAAM;aACZ,OAAO,CAAC,EAAE,OAAO;gBAFjC,OAAO,EAAE,MAAM,EACC,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,OAAO,YAAA;CAQpC;AAED;;GAEG;AACH,qBAAa,kBAAmB,SAAQ,YAAY;gBACtC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;CAOzC;AAED;;GAEG;AACH,qBAAa,eAAgB,SAAQ,YAAY;gBACnC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CAQ7C;AAED;;GAEG;AACH,qBAAa,uBAAwB,SAAQ,YAAY;gBAC3C,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK;CAQxC;AAED;;GAEG;AACH,qBAAa,WAAY,SAAQ,YAAY;gBAC/B,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK;CAO7C;AAED;;GAEG;AACH,qBAAa,oBAAqB,SAAQ,YAAY;gBACxC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CAQ7C;AAED;;GAEG;AACH,qBAAa,qBAAsB,SAAQ,YAAY;gBACzC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM;CAQnE;AAED;;GAEG;AACH,qBAAa,qBAAsB,SAAQ,YAAY;gBACzC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,EAAE,OAAO;CAQ1D;AAED;;GAEG;AACH,qBAAa,kBAAmB,SAAQ,YAAY;gBACtC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM;CAQ7D"}
|
package/dist/errors.js
CHANGED
package/dist/http-helpers.d.ts
CHANGED
|
@@ -12,8 +12,11 @@ export declare function parseNodeQuery(url: URL): Record<string, string>;
|
|
|
12
12
|
* Collect and parse the request body from a Node.js IncomingMessage.
|
|
13
13
|
* Returns parsed JSON if content-type includes "json", otherwise the raw string.
|
|
14
14
|
* Returns undefined for empty bodies.
|
|
15
|
+
* @param req - Node.js IncomingMessage
|
|
16
|
+
* @param headers - Parsed request headers
|
|
17
|
+
* @param maxBodySize - Maximum body size in bytes (default: 10 MB)
|
|
15
18
|
*/
|
|
16
|
-
export declare function collectBody(req: IncomingMessage, headers: Record<string, string
|
|
19
|
+
export declare function collectBody(req: IncomingMessage, headers: Record<string, string>, maxBodySize?: number): Promise<unknown>;
|
|
17
20
|
/**
|
|
18
21
|
* Write a Schmock Response to a Node.js ServerResponse.
|
|
19
22
|
* Serializes non-string bodies as JSON and sets content-type when missing.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http-helpers.d.ts","sourceRoot":"","sources":["../src/http-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEjE;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAQ7E;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAM/D;
|
|
1
|
+
{"version":3,"file":"http-helpers.d.ts","sourceRoot":"","sources":["../src/http-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEjE;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAQ7E;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAM/D;AAKD;;;;;;;GAOG;AACH,wBAAgB,WAAW,CACzB,GAAG,EAAE,eAAe,EACpB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,WAAW,SAAwB,GAClC,OAAO,CAAC,OAAO,CAAC,CAqClB;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,cAAc,EACnB,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAC1B,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GACpC,IAAI,CAuBN"}
|
package/dist/http-helpers.js
CHANGED
|
@@ -21,15 +21,30 @@ export function parseNodeQuery(url) {
|
|
|
21
21
|
});
|
|
22
22
|
return query;
|
|
23
23
|
}
|
|
24
|
+
/** Default body size limit: 10 MB */
|
|
25
|
+
const DEFAULT_MAX_BODY_SIZE = 10 * 1024 * 1024;
|
|
24
26
|
/**
|
|
25
27
|
* Collect and parse the request body from a Node.js IncomingMessage.
|
|
26
28
|
* Returns parsed JSON if content-type includes "json", otherwise the raw string.
|
|
27
29
|
* Returns undefined for empty bodies.
|
|
30
|
+
* @param req - Node.js IncomingMessage
|
|
31
|
+
* @param headers - Parsed request headers
|
|
32
|
+
* @param maxBodySize - Maximum body size in bytes (default: 10 MB)
|
|
28
33
|
*/
|
|
29
|
-
export function collectBody(req, headers) {
|
|
30
|
-
return new Promise((resolve) => {
|
|
34
|
+
export function collectBody(req, headers, maxBodySize = DEFAULT_MAX_BODY_SIZE) {
|
|
35
|
+
return new Promise((resolve, reject) => {
|
|
31
36
|
const chunks = [];
|
|
32
|
-
|
|
37
|
+
let totalSize = 0;
|
|
38
|
+
req.on("error", reject);
|
|
39
|
+
req.on("data", (chunk) => {
|
|
40
|
+
totalSize += chunk.length;
|
|
41
|
+
if (totalSize > maxBodySize) {
|
|
42
|
+
req.destroy();
|
|
43
|
+
reject(Object.assign(new Error("Request body too large"), { status: 413 }));
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
chunks.push(chunk);
|
|
47
|
+
});
|
|
33
48
|
req.on("end", () => {
|
|
34
49
|
const raw = Buffer.concat(chunks).toString();
|
|
35
50
|
if (!raw) {
|
package/dist/index.d.ts
CHANGED
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
* @returns A callable mock instance
|
|
23
23
|
*/
|
|
24
24
|
export declare function schmock(config?: Schmock.GlobalConfig): Schmock.CallableMockInstance;
|
|
25
|
-
export { HTTP_METHODS, isHttpMethod, isStatusTuple, ROUTE_NOT_FOUND_CODE, toHttpMethod, } from "./constants.js";
|
|
25
|
+
export { HTTP_METHODS, isHttpMethod, isStatusTuple, ROUTE_NOT_FOUND_CODE, toHttpMethod, toRouteKey, } from "./constants.js";
|
|
26
26
|
export { PluginError, ResourceLimitError, ResponseGenerationError, RouteDefinitionError, RouteNotFoundError, RouteParseError, SchemaGenerationError, SchemaValidationError, SchmockError, } from "./errors.js";
|
|
27
27
|
export { collectBody, parseNodeHeaders, parseNodeQuery, writeSchmockResponse, } from "./http-helpers.js";
|
|
28
28
|
export type { CallableMockInstance, Generator, GeneratorFunction, GlobalConfig, HttpMethod, Plugin, PluginContext, PluginResult, RequestContext, RequestOptions, RequestRecord, Response, ResponseBody, ResponseResult, RouteConfig, RouteInfo, RouteKey, ServerInfo, StaticData, } from "./types.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,OAAO,CACrB,MAAM,CAAC,EAAE,OAAO,CAAC,YAAY,GAC5B,OAAO,CAAC,oBAAoB,CAmD9B;AAGD,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,oBAAoB,EACpB,YAAY,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,OAAO,CACrB,MAAM,CAAC,EAAE,OAAO,CAAC,YAAY,GAC5B,OAAO,CAAC,oBAAoB,CAmD9B;AAGD,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,oBAAoB,EACpB,YAAY,EACZ,UAAU,GACX,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,WAAW,EACX,kBAAkB,EAClB,uBAAuB,EACvB,oBAAoB,EACpB,kBAAkB,EAClB,eAAe,EACf,qBAAqB,EACrB,qBAAqB,EACrB,YAAY,GACb,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,WAAW,EACX,gBAAgB,EAChB,cAAc,EACd,oBAAoB,GACrB,MAAM,mBAAmB,CAAC;AAE3B,YAAY,EACV,oBAAoB,EACpB,SAAS,EACT,iBAAiB,EACjB,YAAY,EACZ,UAAU,EACV,MAAM,EACN,aAAa,EACb,YAAY,EACZ,cAAc,EACd,cAAc,EACd,aAAa,EACb,QAAQ,EACR,YAAY,EACZ,cAAc,EACd,WAAW,EACX,SAAS,EACT,QAAQ,EACR,UAAU,EACV,UAAU,GACX,MAAM,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -59,7 +59,7 @@ export function schmock(config) {
|
|
|
59
59
|
return callableInstance;
|
|
60
60
|
}
|
|
61
61
|
// Re-export constants and utilities
|
|
62
|
-
export { HTTP_METHODS, isHttpMethod, isStatusTuple, ROUTE_NOT_FOUND_CODE, toHttpMethod, } from "./constants.js";
|
|
62
|
+
export { HTTP_METHODS, isHttpMethod, isStatusTuple, ROUTE_NOT_FOUND_CODE, toHttpMethod, toRouteKey, } from "./constants.js";
|
|
63
63
|
// Re-export errors
|
|
64
64
|
export { PluginError, ResourceLimitError, ResponseGenerationError, RouteDefinitionError, RouteNotFoundError, RouteParseError, SchemaGenerationError, SchemaValidationError, SchmockError, } from "./errors.js";
|
|
65
65
|
// Re-export HTTP server helpers
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** Structural typing — DebugLogger satisfies this without an import */
|
|
2
|
+
interface PipelineLogger {
|
|
3
|
+
log(category: string, message: string, data?: unknown): void;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Run all registered plugins in sequence
|
|
7
|
+
* First plugin to set response becomes generator, subsequent plugins transform
|
|
8
|
+
* Handles plugin errors via onError hooks
|
|
9
|
+
*/
|
|
10
|
+
export declare function runPluginPipeline(plugins: Schmock.Plugin[], context: Schmock.PluginContext, initialResponse: unknown, logger: PipelineLogger): Promise<{
|
|
11
|
+
context: Schmock.PluginContext;
|
|
12
|
+
response?: unknown;
|
|
13
|
+
}>;
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=plugin-pipeline.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-pipeline.d.ts","sourceRoot":"","sources":["../src/plugin-pipeline.ts"],"names":[],"mappings":"AAEA,uEAAuE;AACvE,UAAU,cAAc;IACtB,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;CAC9D;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,OAAO,CAAC,MAAM,EAAE,EACzB,OAAO,EAAE,OAAO,CAAC,aAAa,EAC9B,eAAe,EAAE,OAAO,EACxB,MAAM,EAAE,cAAc,GACrB,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC,aAAa,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,CAkFjE"}
|