@schmock/core 1.4.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/builder.d.ts.map +1 -1
- package/dist/builder.js +6 -45
- package/dist/http-helpers.d.ts +22 -0
- package/dist/http-helpers.d.ts.map +1 -0
- package/dist/http-helpers.js +75 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/types.d.ts +9 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/builder.ts +13 -51
- package/src/http-helpers.ts +91 -0
- package/src/index.ts +7 -0
- package/src/types.ts +9 -0
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":"AAkFA;;;;GAIG;AACH,qBAAa,oBAAoB;IAWnB,OAAO,CAAC,YAAY;IAVhC,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;IACnD,OAAO,CAAC,SAAS,CAAmD;gBAEhD,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;IAqFP,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,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,GAAG,IAAI;IAU1D,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,GAAG,IAAI;IAK3D,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;IAuCrE,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;IAgN5B;;;;OAIG;YACW,UAAU;IAgBxB;;;;;;;OAOG;IACH,OAAO,CAAC,aAAa;IAwDrB;;;;;;;;;;OAUG;YACW,iBAAiB;IAqG/B;;;;;;;;OAQG;IACH,OAAO,CAAC,SAAS;IA0BjB;;;;;;;OAOG;IACH,OAAO,CAAC,aAAa;CActB"}
|
package/dist/builder.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createServer } from "node:http";
|
|
2
2
|
import { isStatusTuple, toHttpMethod } from "./constants.js";
|
|
3
3
|
import { PluginError, RouteDefinitionError, RouteNotFoundError, SchmockError, } from "./errors.js";
|
|
4
|
+
import { collectBody, parseNodeHeaders, parseNodeQuery, writeSchmockResponse, } from "./http-helpers.js";
|
|
4
5
|
import { parseRouteKey } from "./parser.js";
|
|
5
6
|
function errorMessage(error) {
|
|
6
7
|
return error instanceof Error ? error.message : "Unknown error";
|
|
@@ -252,51 +253,11 @@ export class CallableMockInstance {
|
|
|
252
253
|
const url = new URL(req.url ?? "/", `http://${req.headers.host}`);
|
|
253
254
|
const method = toHttpMethod(req.method ?? "GET");
|
|
254
255
|
const path = url.pathname;
|
|
255
|
-
const headers =
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
}
|
|
261
|
-
const query = {};
|
|
262
|
-
url.searchParams.forEach((value, key) => {
|
|
263
|
-
query[key] = value;
|
|
264
|
-
});
|
|
265
|
-
const chunks = [];
|
|
266
|
-
req.on("data", (chunk) => chunks.push(chunk));
|
|
267
|
-
req.on("end", () => {
|
|
268
|
-
const raw = Buffer.concat(chunks).toString();
|
|
269
|
-
let body;
|
|
270
|
-
const contentType = headers["content-type"] ?? "";
|
|
271
|
-
if (raw && contentType.includes("json")) {
|
|
272
|
-
try {
|
|
273
|
-
body = JSON.parse(raw);
|
|
274
|
-
}
|
|
275
|
-
catch {
|
|
276
|
-
body = raw;
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
else if (raw) {
|
|
280
|
-
body = raw;
|
|
281
|
-
}
|
|
282
|
-
void this.handle(method, path, { headers, body, query }).then((schmockResponse) => {
|
|
283
|
-
const responseHeaders = {
|
|
284
|
-
...schmockResponse.headers,
|
|
285
|
-
};
|
|
286
|
-
if (!responseHeaders["content-type"] &&
|
|
287
|
-
schmockResponse.body !== undefined &&
|
|
288
|
-
typeof schmockResponse.body !== "string") {
|
|
289
|
-
responseHeaders["content-type"] = "application/json";
|
|
290
|
-
}
|
|
291
|
-
const responseBody = schmockResponse.body === undefined
|
|
292
|
-
? undefined
|
|
293
|
-
: typeof schmockResponse.body === "string"
|
|
294
|
-
? schmockResponse.body
|
|
295
|
-
: JSON.stringify(schmockResponse.body);
|
|
296
|
-
res.writeHead(schmockResponse.status, responseHeaders);
|
|
297
|
-
res.end(responseBody);
|
|
298
|
-
});
|
|
299
|
-
});
|
|
256
|
+
const headers = parseNodeHeaders(req);
|
|
257
|
+
const query = parseNodeQuery(url);
|
|
258
|
+
void collectBody(req, headers).then((body) => this.handle(method, path, { headers, body, query }).then((schmockResponse) => {
|
|
259
|
+
writeSchmockResponse(res, schmockResponse);
|
|
260
|
+
}));
|
|
300
261
|
});
|
|
301
262
|
this.server = httpServer;
|
|
302
263
|
return new Promise((resolve, reject) => {
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
2
|
+
/**
|
|
3
|
+
* Convert Node.js IncomingMessage headers to a flat Record<string, string>.
|
|
4
|
+
* Drops array-valued headers (keeps only string values).
|
|
5
|
+
*/
|
|
6
|
+
export declare function parseNodeHeaders(req: IncomingMessage): Record<string, string>;
|
|
7
|
+
/**
|
|
8
|
+
* Extract query parameters from a URL as a flat Record<string, string>.
|
|
9
|
+
*/
|
|
10
|
+
export declare function parseNodeQuery(url: URL): Record<string, string>;
|
|
11
|
+
/**
|
|
12
|
+
* Collect and parse the request body from a Node.js IncomingMessage.
|
|
13
|
+
* Returns parsed JSON if content-type includes "json", otherwise the raw string.
|
|
14
|
+
* Returns undefined for empty bodies.
|
|
15
|
+
*/
|
|
16
|
+
export declare function collectBody(req: IncomingMessage, headers: Record<string, string>): Promise<unknown>;
|
|
17
|
+
/**
|
|
18
|
+
* Write a Schmock Response to a Node.js ServerResponse.
|
|
19
|
+
* Serializes non-string bodies as JSON and sets content-type when missing.
|
|
20
|
+
*/
|
|
21
|
+
export declare function writeSchmockResponse(res: ServerResponse, response: Schmock.Response, extraHeaders?: Record<string, string>): void;
|
|
22
|
+
//# sourceMappingURL=http-helpers.d.ts.map
|
|
@@ -0,0 +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;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CACzB,GAAG,EAAE,eAAe,EACpB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC9B,OAAO,CAAC,OAAO,CAAC,CAsBlB;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"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convert Node.js IncomingMessage headers to a flat Record<string, string>.
|
|
3
|
+
* Drops array-valued headers (keeps only string values).
|
|
4
|
+
*/
|
|
5
|
+
export function parseNodeHeaders(req) {
|
|
6
|
+
const headers = {};
|
|
7
|
+
for (const [key, value] of Object.entries(req.headers)) {
|
|
8
|
+
if (typeof value === "string") {
|
|
9
|
+
headers[key] = value;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
return headers;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Extract query parameters from a URL as a flat Record<string, string>.
|
|
16
|
+
*/
|
|
17
|
+
export function parseNodeQuery(url) {
|
|
18
|
+
const query = {};
|
|
19
|
+
url.searchParams.forEach((value, key) => {
|
|
20
|
+
query[key] = value;
|
|
21
|
+
});
|
|
22
|
+
return query;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Collect and parse the request body from a Node.js IncomingMessage.
|
|
26
|
+
* Returns parsed JSON if content-type includes "json", otherwise the raw string.
|
|
27
|
+
* Returns undefined for empty bodies.
|
|
28
|
+
*/
|
|
29
|
+
export function collectBody(req, headers) {
|
|
30
|
+
return new Promise((resolve) => {
|
|
31
|
+
const chunks = [];
|
|
32
|
+
req.on("data", (chunk) => chunks.push(chunk));
|
|
33
|
+
req.on("end", () => {
|
|
34
|
+
const raw = Buffer.concat(chunks).toString();
|
|
35
|
+
if (!raw) {
|
|
36
|
+
resolve(undefined);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const contentType = headers["content-type"] ?? "";
|
|
40
|
+
if (contentType.includes("json")) {
|
|
41
|
+
try {
|
|
42
|
+
resolve(JSON.parse(raw));
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
resolve(raw);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
resolve(raw);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Write a Schmock Response to a Node.js ServerResponse.
|
|
56
|
+
* Serializes non-string bodies as JSON and sets content-type when missing.
|
|
57
|
+
*/
|
|
58
|
+
export function writeSchmockResponse(res, response, extraHeaders) {
|
|
59
|
+
const responseHeaders = {
|
|
60
|
+
...response.headers,
|
|
61
|
+
...extraHeaders,
|
|
62
|
+
};
|
|
63
|
+
if (!responseHeaders["content-type"] &&
|
|
64
|
+
response.body !== undefined &&
|
|
65
|
+
typeof response.body !== "string") {
|
|
66
|
+
responseHeaders["content-type"] = "application/json";
|
|
67
|
+
}
|
|
68
|
+
const responseBody = response.body === undefined
|
|
69
|
+
? undefined
|
|
70
|
+
: typeof response.body === "string"
|
|
71
|
+
? response.body
|
|
72
|
+
: JSON.stringify(response.body);
|
|
73
|
+
res.writeHead(response.status, responseHeaders);
|
|
74
|
+
res.end(responseBody);
|
|
75
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -24,5 +24,6 @@
|
|
|
24
24
|
export declare function schmock(config?: Schmock.GlobalConfig): Schmock.CallableMockInstance;
|
|
25
25
|
export { HTTP_METHODS, isHttpMethod, isStatusTuple, ROUTE_NOT_FOUND_CODE, toHttpMethod, } from "./constants.js";
|
|
26
26
|
export { PluginError, ResourceLimitError, ResponseGenerationError, RouteDefinitionError, RouteNotFoundError, RouteParseError, SchemaGenerationError, SchemaValidationError, SchmockError, } from "./errors.js";
|
|
27
|
+
export { collectBody, parseNodeHeaders, parseNodeQuery, writeSchmockResponse, } from "./http-helpers.js";
|
|
27
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";
|
|
28
29
|
//# 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":"AAEA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,OAAO,CACrB,MAAM,CAAC,EAAE,OAAO,CAAC,YAAY,GAC5B,OAAO,CAAC,oBAAoB,CA6C9B;AAGD,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,oBAAoB,EACpB,YAAY,GACb,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,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"}
|
|
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,CA6C9B;AAGD,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,oBAAoB,EACpB,YAAY,GACb,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
|
@@ -62,3 +62,5 @@ export function schmock(config) {
|
|
|
62
62
|
export { HTTP_METHODS, isHttpMethod, isStatusTuple, ROUTE_NOT_FOUND_CODE, toHttpMethod, } from "./constants.js";
|
|
63
63
|
// Re-export errors
|
|
64
64
|
export { PluginError, ResourceLimitError, ResponseGenerationError, RouteDefinitionError, RouteNotFoundError, RouteParseError, SchemaGenerationError, SchemaValidationError, SchmockError, } from "./errors.js";
|
|
65
|
+
// Re-export HTTP server helpers
|
|
66
|
+
export { collectBody, parseNodeHeaders, parseNodeQuery, writeSchmockResponse, } from "./http-helpers.js";
|
package/dist/types.d.ts
CHANGED
|
@@ -17,4 +17,13 @@ export type StaticData = Schmock.StaticData;
|
|
|
17
17
|
export type RequestRecord = Schmock.RequestRecord;
|
|
18
18
|
export type ServerInfo = Schmock.ServerInfo;
|
|
19
19
|
export type RouteInfo = Schmock.RouteInfo;
|
|
20
|
+
export type SchemaGenerationContext = Schmock.SchemaGenerationContext;
|
|
21
|
+
export type FakerPluginOptions = Schmock.FakerPluginOptions;
|
|
22
|
+
export type ExpressAdapterOptions = Schmock.ExpressAdapterOptions;
|
|
23
|
+
export type AngularAdapterOptions = Schmock.AngularAdapterOptions;
|
|
24
|
+
export type OpenApiOptions = Schmock.OpenApiOptions;
|
|
25
|
+
export type SeedSource = Schmock.SeedSource;
|
|
26
|
+
export type SeedConfig = Schmock.SeedConfig;
|
|
27
|
+
export type CliOptions = Schmock.CliOptions;
|
|
28
|
+
export type CliServer = Schmock.CliServer;
|
|
20
29
|
//# 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":"AAGA,MAAM,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;AAC5C,MAAM,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;AACxC,MAAM,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC;AAChD,MAAM,MAAM,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC;AACpD,MAAM,MAAM,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC;AACpD,MAAM,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;AACxC,MAAM,MAAM,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC;AACpD,MAAM,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC;AAChD,MAAM,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;AAC9C,MAAM,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;AAC1C,MAAM,MAAM,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC;AAC1D,MAAM,MAAM,oBAAoB,GAAG,OAAO,CAAC,oBAAoB,CAAC;AAChE,MAAM,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;AACpC,MAAM,MAAM,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC;AAClD,MAAM,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC;AAChD,MAAM,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;AAC5C,MAAM,MAAM,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC;AAClD,MAAM,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;AAC5C,MAAM,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;AAC5C,MAAM,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;AACxC,MAAM,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC;AAChD,MAAM,MAAM,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC;AACpD,MAAM,MAAM,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC;AACpD,MAAM,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;AACxC,MAAM,MAAM,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC;AACpD,MAAM,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC;AAChD,MAAM,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;AAC9C,MAAM,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;AAC1C,MAAM,MAAM,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC;AAC1D,MAAM,MAAM,oBAAoB,GAAG,OAAO,CAAC,oBAAoB,CAAC;AAChE,MAAM,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;AACpC,MAAM,MAAM,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC;AAClD,MAAM,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC;AAChD,MAAM,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;AAC5C,MAAM,MAAM,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC;AAClD,MAAM,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;AAC5C,MAAM,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;AAC1C,MAAM,MAAM,uBAAuB,GAAG,OAAO,CAAC,uBAAuB,CAAC;AACtE,MAAM,MAAM,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,CAAC;AAC5D,MAAM,MAAM,qBAAqB,GAAG,OAAO,CAAC,qBAAqB,CAAC;AAClE,MAAM,MAAM,qBAAqB,GAAG,OAAO,CAAC,qBAAqB,CAAC;AAClE,MAAM,MAAM,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC;AACpD,MAAM,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;AAC5C,MAAM,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;AAC5C,MAAM,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;AAC5C,MAAM,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC"}
|
package/package.json
CHANGED
package/src/builder.ts
CHANGED
|
@@ -7,6 +7,12 @@ import {
|
|
|
7
7
|
RouteNotFoundError,
|
|
8
8
|
SchmockError,
|
|
9
9
|
} from "./errors.js";
|
|
10
|
+
import {
|
|
11
|
+
collectBody,
|
|
12
|
+
parseNodeHeaders,
|
|
13
|
+
parseNodeQuery,
|
|
14
|
+
writeSchmockResponse,
|
|
15
|
+
} from "./http-helpers.js";
|
|
10
16
|
import { parseRouteKey } from "./parser.js";
|
|
11
17
|
|
|
12
18
|
function errorMessage(error: unknown): string {
|
|
@@ -341,60 +347,16 @@ export class CallableMockInstance {
|
|
|
341
347
|
const url = new URL(req.url ?? "/", `http://${req.headers.host}`);
|
|
342
348
|
const method = toHttpMethod(req.method ?? "GET");
|
|
343
349
|
const path = url.pathname;
|
|
350
|
+
const headers = parseNodeHeaders(req);
|
|
351
|
+
const query = parseNodeQuery(url);
|
|
344
352
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
if (typeof value === "string") {
|
|
348
|
-
headers[key] = value;
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
const query: Record<string, string> = {};
|
|
353
|
-
url.searchParams.forEach((value, key) => {
|
|
354
|
-
query[key] = value;
|
|
355
|
-
});
|
|
356
|
-
|
|
357
|
-
const chunks: Buffer[] = [];
|
|
358
|
-
req.on("data", (chunk: Buffer) => chunks.push(chunk));
|
|
359
|
-
req.on("end", () => {
|
|
360
|
-
const raw = Buffer.concat(chunks).toString();
|
|
361
|
-
let body: unknown;
|
|
362
|
-
const contentType = headers["content-type"] ?? "";
|
|
363
|
-
if (raw && contentType.includes("json")) {
|
|
364
|
-
try {
|
|
365
|
-
body = JSON.parse(raw);
|
|
366
|
-
} catch {
|
|
367
|
-
body = raw;
|
|
368
|
-
}
|
|
369
|
-
} else if (raw) {
|
|
370
|
-
body = raw;
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
void this.handle(method, path, { headers, body, query }).then(
|
|
353
|
+
void collectBody(req, headers).then((body) =>
|
|
354
|
+
this.handle(method, path, { headers, body, query }).then(
|
|
374
355
|
(schmockResponse) => {
|
|
375
|
-
|
|
376
|
-
...schmockResponse.headers,
|
|
377
|
-
};
|
|
378
|
-
if (
|
|
379
|
-
!responseHeaders["content-type"] &&
|
|
380
|
-
schmockResponse.body !== undefined &&
|
|
381
|
-
typeof schmockResponse.body !== "string"
|
|
382
|
-
) {
|
|
383
|
-
responseHeaders["content-type"] = "application/json";
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
const responseBody =
|
|
387
|
-
schmockResponse.body === undefined
|
|
388
|
-
? undefined
|
|
389
|
-
: typeof schmockResponse.body === "string"
|
|
390
|
-
? schmockResponse.body
|
|
391
|
-
: JSON.stringify(schmockResponse.body);
|
|
392
|
-
|
|
393
|
-
res.writeHead(schmockResponse.status, responseHeaders);
|
|
394
|
-
res.end(responseBody);
|
|
356
|
+
writeSchmockResponse(res, schmockResponse);
|
|
395
357
|
},
|
|
396
|
-
)
|
|
397
|
-
|
|
358
|
+
),
|
|
359
|
+
);
|
|
398
360
|
});
|
|
399
361
|
|
|
400
362
|
this.server = httpServer;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Convert Node.js IncomingMessage headers to a flat Record<string, string>.
|
|
5
|
+
* Drops array-valued headers (keeps only string values).
|
|
6
|
+
*/
|
|
7
|
+
export function parseNodeHeaders(req: IncomingMessage): Record<string, string> {
|
|
8
|
+
const headers: Record<string, string> = {};
|
|
9
|
+
for (const [key, value] of Object.entries(req.headers)) {
|
|
10
|
+
if (typeof value === "string") {
|
|
11
|
+
headers[key] = value;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
return headers;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Extract query parameters from a URL as a flat Record<string, string>.
|
|
19
|
+
*/
|
|
20
|
+
export function parseNodeQuery(url: URL): Record<string, string> {
|
|
21
|
+
const query: Record<string, string> = {};
|
|
22
|
+
url.searchParams.forEach((value, key) => {
|
|
23
|
+
query[key] = value;
|
|
24
|
+
});
|
|
25
|
+
return query;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Collect and parse the request body from a Node.js IncomingMessage.
|
|
30
|
+
* Returns parsed JSON if content-type includes "json", otherwise the raw string.
|
|
31
|
+
* Returns undefined for empty bodies.
|
|
32
|
+
*/
|
|
33
|
+
export function collectBody(
|
|
34
|
+
req: IncomingMessage,
|
|
35
|
+
headers: Record<string, string>,
|
|
36
|
+
): Promise<unknown> {
|
|
37
|
+
return new Promise((resolve) => {
|
|
38
|
+
const chunks: Buffer[] = [];
|
|
39
|
+
req.on("data", (chunk: Buffer) => chunks.push(chunk));
|
|
40
|
+
req.on("end", () => {
|
|
41
|
+
const raw = Buffer.concat(chunks).toString();
|
|
42
|
+
if (!raw) {
|
|
43
|
+
resolve(undefined);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const contentType = headers["content-type"] ?? "";
|
|
47
|
+
if (contentType.includes("json")) {
|
|
48
|
+
try {
|
|
49
|
+
resolve(JSON.parse(raw));
|
|
50
|
+
} catch {
|
|
51
|
+
resolve(raw);
|
|
52
|
+
}
|
|
53
|
+
} else {
|
|
54
|
+
resolve(raw);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Write a Schmock Response to a Node.js ServerResponse.
|
|
62
|
+
* Serializes non-string bodies as JSON and sets content-type when missing.
|
|
63
|
+
*/
|
|
64
|
+
export function writeSchmockResponse(
|
|
65
|
+
res: ServerResponse,
|
|
66
|
+
response: Schmock.Response,
|
|
67
|
+
extraHeaders?: Record<string, string>,
|
|
68
|
+
): void {
|
|
69
|
+
const responseHeaders: Record<string, string> = {
|
|
70
|
+
...response.headers,
|
|
71
|
+
...extraHeaders,
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
if (
|
|
75
|
+
!responseHeaders["content-type"] &&
|
|
76
|
+
response.body !== undefined &&
|
|
77
|
+
typeof response.body !== "string"
|
|
78
|
+
) {
|
|
79
|
+
responseHeaders["content-type"] = "application/json";
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const responseBody =
|
|
83
|
+
response.body === undefined
|
|
84
|
+
? undefined
|
|
85
|
+
: typeof response.body === "string"
|
|
86
|
+
? response.body
|
|
87
|
+
: JSON.stringify(response.body);
|
|
88
|
+
|
|
89
|
+
res.writeHead(response.status, responseHeaders);
|
|
90
|
+
res.end(responseBody);
|
|
91
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -92,6 +92,13 @@ export {
|
|
|
92
92
|
SchemaValidationError,
|
|
93
93
|
SchmockError,
|
|
94
94
|
} from "./errors.js";
|
|
95
|
+
// Re-export HTTP server helpers
|
|
96
|
+
export {
|
|
97
|
+
collectBody,
|
|
98
|
+
parseNodeHeaders,
|
|
99
|
+
parseNodeQuery,
|
|
100
|
+
writeSchmockResponse,
|
|
101
|
+
} from "./http-helpers.js";
|
|
95
102
|
// Re-export types
|
|
96
103
|
export type {
|
|
97
104
|
CallableMockInstance,
|
package/src/types.ts
CHANGED
|
@@ -20,3 +20,12 @@ export type StaticData = Schmock.StaticData;
|
|
|
20
20
|
export type RequestRecord = Schmock.RequestRecord;
|
|
21
21
|
export type ServerInfo = Schmock.ServerInfo;
|
|
22
22
|
export type RouteInfo = Schmock.RouteInfo;
|
|
23
|
+
export type SchemaGenerationContext = Schmock.SchemaGenerationContext;
|
|
24
|
+
export type FakerPluginOptions = Schmock.FakerPluginOptions;
|
|
25
|
+
export type ExpressAdapterOptions = Schmock.ExpressAdapterOptions;
|
|
26
|
+
export type AngularAdapterOptions = Schmock.AngularAdapterOptions;
|
|
27
|
+
export type OpenApiOptions = Schmock.OpenApiOptions;
|
|
28
|
+
export type SeedSource = Schmock.SeedSource;
|
|
29
|
+
export type SeedConfig = Schmock.SeedConfig;
|
|
30
|
+
export type CliOptions = Schmock.CliOptions;
|
|
31
|
+
export type CliServer = Schmock.CliServer;
|