@schmock/core 1.7.0 → 1.9.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 CHANGED
@@ -24,8 +24,8 @@ export declare class CallableMockInstance {
24
24
  lastRequest(method?: Schmock.HttpMethod, path?: string): Schmock.RequestRecord | undefined;
25
25
  getRoutes(): Schmock.RouteInfo[];
26
26
  getState(): Record<string, unknown>;
27
- on(event: string, listener: (data: unknown) => void): this;
28
- off(event: string, listener: (data: unknown) => void): this;
27
+ on<E extends Schmock.SchmockEvent>(event: E, listener: (data: Schmock.SchmockEventMap[E]) => void): this;
28
+ off<E extends Schmock.SchmockEvent>(event: E, listener: (data: Schmock.SchmockEventMap[E]) => void): this;
29
29
  private emit;
30
30
  reset(): void;
31
31
  resetHistory(): void;
@@ -1 +1 @@
1
- {"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../src/builder.ts"],"names":[],"mappings":"AA4EA;;;;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;IAmFrE,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"}
1
+ {"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../src/builder.ts"],"names":[],"mappings":"AAkFA;;;;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;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,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;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";
@@ -60,6 +61,7 @@ export class CallableMockInstance {
60
61
  callableRef;
61
62
  server;
62
63
  serverInfo;
64
+ // biome-ignore lint/complexity/noBannedTypes: internal storage for event listeners with varying signatures
63
65
  listeners = new Map();
64
66
  constructor(globalConfig = {}) {
65
67
  this.globalConfig = globalConfig;
@@ -252,51 +254,11 @@ export class CallableMockInstance {
252
254
  const url = new URL(req.url ?? "/", `http://${req.headers.host}`);
253
255
  const method = toHttpMethod(req.method ?? "GET");
254
256
  const path = url.pathname;
255
- const headers = {};
256
- for (const [key, value] of Object.entries(req.headers)) {
257
- if (typeof value === "string") {
258
- headers[key] = value;
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
- });
257
+ const headers = parseNodeHeaders(req);
258
+ const query = parseNodeQuery(url);
259
+ void collectBody(req, headers).then((body) => this.handle(method, path, { headers, body, query }).then((schmockResponse) => {
260
+ writeSchmockResponse(res, schmockResponse);
261
+ }));
300
262
  });
301
263
  this.server = httpServer;
302
264
  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
@@ -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,CAmD9B;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
@@ -42,14 +42,14 @@ export function schmock(config) {
42
42
  reset: instance.reset.bind(instance),
43
43
  resetHistory: instance.resetHistory.bind(instance),
44
44
  resetState: instance.resetState.bind(instance),
45
- on: ((event, listener) => {
45
+ on(event, listener) {
46
46
  instance.on(event, listener);
47
47
  return callableInstance;
48
- }),
49
- off: ((event, listener) => {
48
+ },
49
+ off(event, listener) {
50
50
  instance.off(event, listener);
51
51
  return callableInstance;
52
- }),
52
+ },
53
53
  getRoutes: instance.getRoutes.bind(instance),
54
54
  getState: instance.getState.bind(instance),
55
55
  listen: instance.listen.bind(instance),
@@ -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
@@ -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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@schmock/core",
3
3
  "description": "Core functionality for Schmock",
4
- "version": "1.7.0",
4
+ "version": "1.9.0",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
@@ -33,7 +33,7 @@
33
33
  "license": "MIT",
34
34
  "devDependencies": {
35
35
  "@amiceli/vitest-cucumber": "^6.2.0",
36
- "@types/node": "^25.2.1",
36
+ "@types/node": "^25.2.3",
37
37
  "@vitest/ui": "^4.0.18",
38
38
  "vitest": "^4.0.18"
39
39
  }
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 {
@@ -88,7 +94,8 @@ export class CallableMockInstance {
88
94
  private callableRef: Schmock.CallableMockInstance | undefined;
89
95
  private server: Server | undefined;
90
96
  private serverInfo: Schmock.ServerInfo | undefined;
91
- private listeners = new Map<string, Set<(data: unknown) => void>>();
97
+ // biome-ignore lint/complexity/noBannedTypes: internal storage for event listeners with varying signatures
98
+ private listeners = new Map<string, Set<Function>>();
92
99
 
93
100
  constructor(private globalConfig: Schmock.GlobalConfig = {}) {
94
101
  this.logger = new DebugLogger(globalConfig.debug || false);
@@ -272,7 +279,10 @@ export class CallableMockInstance {
272
279
 
273
280
  // ===== Lifecycle Events =====
274
281
 
275
- on(event: string, listener: (data: unknown) => void): this {
282
+ on<E extends Schmock.SchmockEvent>(
283
+ event: E,
284
+ listener: (data: Schmock.SchmockEventMap[E]) => void,
285
+ ): this {
276
286
  let set = this.listeners.get(event);
277
287
  if (!set) {
278
288
  set = new Set();
@@ -282,7 +292,10 @@ export class CallableMockInstance {
282
292
  return this;
283
293
  }
284
294
 
285
- off(event: string, listener: (data: unknown) => void): this {
295
+ off<E extends Schmock.SchmockEvent>(
296
+ event: E,
297
+ listener: (data: Schmock.SchmockEventMap[E]) => void,
298
+ ): this {
286
299
  this.listeners.get(event)?.delete(listener);
287
300
  return this;
288
301
  }
@@ -341,60 +354,16 @@ export class CallableMockInstance {
341
354
  const url = new URL(req.url ?? "/", `http://${req.headers.host}`);
342
355
  const method = toHttpMethod(req.method ?? "GET");
343
356
  const path = url.pathname;
357
+ const headers = parseNodeHeaders(req);
358
+ const query = parseNodeQuery(url);
344
359
 
345
- const headers: Record<string, string> = {};
346
- for (const [key, value] of Object.entries(req.headers)) {
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(
360
+ void collectBody(req, headers).then((body) =>
361
+ this.handle(method, path, { headers, body, query }).then(
374
362
  (schmockResponse) => {
375
- const responseHeaders: Record<string, string> = {
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);
363
+ writeSchmockResponse(res, schmockResponse);
395
364
  },
396
- );
397
- });
365
+ ),
366
+ );
398
367
  });
399
368
 
400
369
  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
@@ -52,14 +52,20 @@ export function schmock(
52
52
  reset: instance.reset.bind(instance),
53
53
  resetHistory: instance.resetHistory.bind(instance),
54
54
  resetState: instance.resetState.bind(instance),
55
- on: ((event: string, listener: (data: unknown) => void) => {
55
+ on<E extends Schmock.SchmockEvent>(
56
+ event: E,
57
+ listener: (data: Schmock.SchmockEventMap[E]) => void,
58
+ ) {
56
59
  instance.on(event, listener);
57
60
  return callableInstance;
58
- }) as Schmock.CallableMockInstance["on"],
59
- off: ((event: string, listener: (data: unknown) => void) => {
61
+ },
62
+ off<E extends Schmock.SchmockEvent>(
63
+ event: E,
64
+ listener: (data: Schmock.SchmockEventMap[E]) => void,
65
+ ) {
60
66
  instance.off(event, listener);
61
67
  return callableInstance;
62
- }) as Schmock.CallableMockInstance["off"],
68
+ },
63
69
  getRoutes: instance.getRoutes.bind(instance),
64
70
  getState: instance.getState.bind(instance),
65
71
  listen: instance.listen.bind(instance),
@@ -92,6 +98,13 @@ export {
92
98
  SchemaValidationError,
93
99
  SchmockError,
94
100
  } from "./errors.js";
101
+ // Re-export HTTP server helpers
102
+ export {
103
+ collectBody,
104
+ parseNodeHeaders,
105
+ parseNodeQuery,
106
+ writeSchmockResponse,
107
+ } from "./http-helpers.js";
95
108
  // Re-export types
96
109
  export type {
97
110
  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;