@schmock/core 1.0.3 → 1.0.4

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.
@@ -1 +1 @@
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;IA6BjB;;;;;;;OAOG;IACH,OAAO,CAAC,aAAa;CActB"}
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;IAuEP,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;IAuF/B;;;;;;;;OAQG;IACH,OAAO,CAAC,SAAS;IA6BjB;;;;;;;OAOG;IACH,OAAO,CAAC,aAAa;CActB"}
package/dist/builder.js CHANGED
@@ -88,6 +88,11 @@ export class CallableMockInstance {
88
88
  }
89
89
  // Parse the route key to create pattern and extract parameters
90
90
  const parsed = parseRouteKey(route);
91
+ // Check for duplicate routes
92
+ const existing = this.routes.find((r) => r.method === parsed.method && r.path === parsed.path);
93
+ if (existing) {
94
+ this.logger.log("warning", `Duplicate route: ${route} — first registration wins`);
95
+ }
91
96
  // Compile the route
92
97
  const compiledRoute = {
93
98
  pattern: parsed.pattern,
@@ -116,7 +121,7 @@ export class CallableMockInstance {
116
121
  return this;
117
122
  }
118
123
  async handle(method, path, options) {
119
- const requestId = Math.random().toString(36).substring(7);
124
+ const requestId = Math.random().toString(36).substring(2, 10) || "00000000";
120
125
  this.logger.log("request", `[${requestId}] ${method} ${path}`, {
121
126
  headers: options?.headers,
122
127
  query: options?.query,
@@ -365,7 +370,9 @@ export class CallableMockInstance {
365
370
  if (errorResult) {
366
371
  this.logger.log("pipeline", `Plugin ${plugin.name} handled error`);
367
372
  // If error handler returns response, use it and stop pipeline
368
- if (typeof errorResult === "object" && errorResult.status) {
373
+ if (typeof errorResult === "object" &&
374
+ errorResult !== null &&
375
+ "status" in errorResult) {
369
376
  // Return the error response as the current response, stop pipeline
370
377
  response = errorResult;
371
378
  break;
@@ -0,0 +1,6 @@
1
+ import type { HttpMethod } from "./types.js";
2
+ export declare const ROUTE_NOT_FOUND_CODE: "ROUTE_NOT_FOUND";
3
+ export declare const HTTP_METHODS: readonly HttpMethod[];
4
+ export declare function isHttpMethod(method: string): method is HttpMethod;
5
+ export declare function toHttpMethod(method: string): HttpMethod;
6
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,20 @@
1
+ export const ROUTE_NOT_FOUND_CODE = "ROUTE_NOT_FOUND";
2
+ export const HTTP_METHODS = [
3
+ "GET",
4
+ "POST",
5
+ "PUT",
6
+ "DELETE",
7
+ "PATCH",
8
+ "HEAD",
9
+ "OPTIONS",
10
+ ];
11
+ export function isHttpMethod(method) {
12
+ return HTTP_METHODS.includes(method);
13
+ }
14
+ export function toHttpMethod(method) {
15
+ const upper = method.toUpperCase();
16
+ if (!isHttpMethod(upper)) {
17
+ throw new Error(`Invalid HTTP method: "${method}"`);
18
+ }
19
+ return upper;
20
+ }
@@ -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;CAMpC;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;;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
@@ -9,7 +9,9 @@ export class SchmockError extends Error {
9
9
  this.code = code;
10
10
  this.context = context;
11
11
  this.name = "SchmockError";
12
- Error.captureStackTrace(this, this.constructor);
12
+ if (typeof Error.captureStackTrace === "function") {
13
+ Error.captureStackTrace(this, this.constructor);
14
+ }
13
15
  }
14
16
  }
15
17
  /**
package/dist/index.d.ts CHANGED
@@ -23,6 +23,7 @@ import type { CallableMockInstance, GlobalConfig } from "./types.js";
23
23
  * @returns A callable mock instance
24
24
  */
25
25
  export declare function schmock(config?: GlobalConfig): CallableMockInstance;
26
+ export { HTTP_METHODS, isHttpMethod, ROUTE_NOT_FOUND_CODE, toHttpMethod, } from "./constants.js";
26
27
  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";
28
+ export type { CallableMockInstance, Generator, GeneratorFunction, GlobalConfig, HttpMethod, Plugin, PluginContext, PluginResult, RequestContext, RequestOptions, Response, ResponseBody, ResponseResult, RouteConfig, RouteKey, 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":"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"}
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,YAAY,EACZ,YAAY,EACZ,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;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,YAAY,EACZ,cAAc,EACd,WAAW,EACX,QAAQ,EACR,UAAU,GACX,MAAM,YAAY,CAAC"}
package/dist/index.js CHANGED
@@ -38,5 +38,7 @@ export function schmock(config) {
38
38
  callableInstance.handle = instance.handle.bind(instance);
39
39
  return callableInstance;
40
40
  }
41
+ // Re-export constants and utilities
42
+ export { HTTP_METHODS, isHttpMethod, ROUTE_NOT_FOUND_CODE, toHttpMethod, } from "./constants.js";
41
43
  // Re-export errors
42
44
  export { PluginError, ResourceLimitError, ResponseGenerationError, RouteDefinitionError, RouteNotFoundError, RouteParseError, SchemaGenerationError, SchemaValidationError, SchmockError, } from "./errors.js";
package/dist/types.d.ts CHANGED
@@ -39,7 +39,7 @@ export interface Plugin {
39
39
  * @param response - Response from previous plugin (if any)
40
40
  * @returns Updated context and response
41
41
  */
42
- process(context: PluginContext, response?: any): PluginResult | Promise<PluginResult>;
42
+ process(context: PluginContext, response?: unknown): PluginResult | Promise<PluginResult>;
43
43
  /**
44
44
  * Called when an error occurs
45
45
  * Can handle, transform, or suppress errors
@@ -56,7 +56,7 @@ export interface PluginResult {
56
56
  /** Updated context */
57
57
  context: PluginContext;
58
58
  /** Response data (if generated/modified) */
59
- response?: any;
59
+ response?: unknown;
60
60
  }
61
61
  /**
62
62
  * Context passed through plugin pipeline
@@ -65,7 +65,7 @@ export interface PluginContext {
65
65
  /** Request path */
66
66
  path: string;
67
67
  /** Matched route configuration */
68
- route: any;
68
+ route: RouteConfig;
69
69
  /** HTTP method */
70
70
  method: HttpMethod;
71
71
  /** Route parameters */
@@ -75,11 +75,11 @@ export interface PluginContext {
75
75
  /** Request headers */
76
76
  headers: Record<string, string>;
77
77
  /** Request body */
78
- body?: any;
78
+ body?: unknown;
79
79
  /** Shared state between plugins for this request */
80
- state: Map<string, any>;
80
+ state: Map<string, unknown>;
81
81
  /** Route-specific state */
82
- routeState?: any;
82
+ routeState?: Record<string, unknown>;
83
83
  }
84
84
  /**
85
85
  * Global configuration options for the mock instance
@@ -92,7 +92,7 @@ export interface GlobalConfig {
92
92
  /** Enable debug mode for detailed logging */
93
93
  debug?: boolean;
94
94
  /** Initial shared state object */
95
- state?: any;
95
+ state?: Record<string, unknown>;
96
96
  }
97
97
  /**
98
98
  * Route-specific configuration options
@@ -111,10 +111,14 @@ export type Generator = GeneratorFunction | StaticData | JSONSchema;
111
111
  * Function that generates responses
112
112
  */
113
113
  export type GeneratorFunction = (context: RequestContext) => ResponseResult | Promise<ResponseResult>;
114
+ /**
115
+ * Response body type alias
116
+ */
117
+ export type ResponseBody = unknown;
114
118
  /**
115
119
  * Static data (non-function) that gets returned as-is
116
120
  */
117
- export type StaticData = any;
121
+ export type StaticData = string | number | boolean | null | undefined | Record<string, unknown> | unknown[];
118
122
  /**
119
123
  * Context passed to generator functions
120
124
  */
@@ -130,9 +134,9 @@ export interface RequestContext {
130
134
  /** Request headers */
131
135
  headers: Record<string, string>;
132
136
  /** Request body (for POST, PUT, PATCH) */
133
- body?: any;
137
+ body?: unknown;
134
138
  /** Shared mutable state */
135
- state: any;
139
+ state: Record<string, unknown>;
136
140
  }
137
141
  /**
138
142
  * Response result types:
@@ -140,13 +144,13 @@ export interface RequestContext {
140
144
  * - [status, body]: custom status with body
141
145
  * - [status, body, headers]: custom status, body, and headers
142
146
  */
143
- export type ResponseResult = any | [number, any] | [number, any, Record<string, string>];
147
+ export type ResponseResult = ResponseBody | [number, ResponseBody] | [number, ResponseBody, Record<string, string>];
144
148
  /**
145
149
  * Response object returned by handle method
146
150
  */
147
151
  export interface Response {
148
152
  status: number;
149
- body: any;
153
+ body: unknown;
150
154
  headers: Record<string, string>;
151
155
  }
152
156
  /**
@@ -154,7 +158,7 @@ export interface Response {
154
158
  */
155
159
  export interface RequestOptions {
156
160
  headers?: Record<string, string>;
157
- body?: any;
161
+ body?: unknown;
158
162
  query?: Record<string, string>;
159
163
  }
160
164
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
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.3",
4
+ "version": "1.0.4",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
@@ -31,9 +31,9 @@
31
31
  },
32
32
  "license": "MIT",
33
33
  "devDependencies": {
34
- "@amiceli/vitest-cucumber": "^6.1.0",
35
- "@types/node": "^25.0.0",
36
- "@vitest/ui": "^4.0.15",
34
+ "@amiceli/vitest-cucumber": "^6.2.0",
35
+ "@types/node": "^25.1.0",
36
+ "@vitest/ui": "^4.0.16",
37
37
  "vitest": "^4.0.15"
38
38
  }
39
39
  }
package/src/builder.ts CHANGED
@@ -128,6 +128,17 @@ export class CallableMockInstance {
128
128
  // Parse the route key to create pattern and extract parameters
129
129
  const parsed = parseRouteKey(route);
130
130
 
131
+ // Check for duplicate routes
132
+ const existing = this.routes.find(
133
+ (r) => r.method === parsed.method && r.path === parsed.path,
134
+ );
135
+ if (existing) {
136
+ this.logger.log(
137
+ "warning",
138
+ `Duplicate route: ${route} — first registration wins`,
139
+ );
140
+ }
141
+
131
142
  // Compile the route
132
143
  const compiledRoute: CompiledCallableRoute = {
133
144
  pattern: parsed.pattern,
@@ -168,7 +179,7 @@ export class CallableMockInstance {
168
179
  path: string,
169
180
  options?: RequestOptions,
170
181
  ): Promise<Response> {
171
- const requestId = Math.random().toString(36).substring(7);
182
+ const requestId = Math.random().toString(36).substring(2, 10) || "00000000";
172
183
  this.logger.log("request", `[${requestId}] ${method} ${path}`, {
173
184
  headers: options?.headers,
174
185
  query: options?.query,
@@ -502,7 +513,11 @@ export class CallableMockInstance {
502
513
  `Plugin ${plugin.name} handled error`,
503
514
  );
504
515
  // If error handler returns response, use it and stop pipeline
505
- if (typeof errorResult === "object" && errorResult.status) {
516
+ if (
517
+ typeof errorResult === "object" &&
518
+ errorResult !== null &&
519
+ "status" in errorResult
520
+ ) {
506
521
  // Return the error response as the current response, stop pipeline
507
522
  response = errorResult;
508
523
  break;
@@ -0,0 +1,59 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import {
3
+ HTTP_METHODS,
4
+ isHttpMethod,
5
+ ROUTE_NOT_FOUND_CODE,
6
+ toHttpMethod,
7
+ } from "./constants";
8
+
9
+ describe("constants", () => {
10
+ it("exports ROUTE_NOT_FOUND_CODE", () => {
11
+ expect(ROUTE_NOT_FOUND_CODE).toBe("ROUTE_NOT_FOUND");
12
+ });
13
+
14
+ it("exports all HTTP methods", () => {
15
+ expect(HTTP_METHODS).toEqual([
16
+ "GET",
17
+ "POST",
18
+ "PUT",
19
+ "DELETE",
20
+ "PATCH",
21
+ "HEAD",
22
+ "OPTIONS",
23
+ ]);
24
+ });
25
+ });
26
+
27
+ describe("isHttpMethod", () => {
28
+ it("returns true for valid HTTP methods", () => {
29
+ for (const method of HTTP_METHODS) {
30
+ expect(isHttpMethod(method)).toBe(true);
31
+ }
32
+ });
33
+
34
+ it("returns false for invalid methods", () => {
35
+ expect(isHttpMethod("INVALID")).toBe(false);
36
+ expect(isHttpMethod("")).toBe(false);
37
+ expect(isHttpMethod("get")).toBe(false);
38
+ });
39
+ });
40
+
41
+ describe("toHttpMethod", () => {
42
+ it("converts lowercase to uppercase", () => {
43
+ expect(toHttpMethod("get")).toBe("GET");
44
+ expect(toHttpMethod("post")).toBe("POST");
45
+ expect(toHttpMethod("delete")).toBe("DELETE");
46
+ });
47
+
48
+ it("returns already uppercase methods", () => {
49
+ expect(toHttpMethod("GET")).toBe("GET");
50
+ expect(toHttpMethod("PATCH")).toBe("PATCH");
51
+ });
52
+
53
+ it("throws for invalid methods", () => {
54
+ expect(() => toHttpMethod("INVALID")).toThrow(
55
+ 'Invalid HTTP method: "INVALID"',
56
+ );
57
+ expect(() => toHttpMethod("")).toThrow('Invalid HTTP method: ""');
58
+ });
59
+ });
@@ -0,0 +1,25 @@
1
+ import type { HttpMethod } from "./types.js";
2
+
3
+ export const ROUTE_NOT_FOUND_CODE = "ROUTE_NOT_FOUND" as const;
4
+
5
+ export const HTTP_METHODS: readonly HttpMethod[] = [
6
+ "GET",
7
+ "POST",
8
+ "PUT",
9
+ "DELETE",
10
+ "PATCH",
11
+ "HEAD",
12
+ "OPTIONS",
13
+ ] as const;
14
+
15
+ export function isHttpMethod(method: string): method is HttpMethod {
16
+ return HTTP_METHODS.includes(method as HttpMethod);
17
+ }
18
+
19
+ export function toHttpMethod(method: string): HttpMethod {
20
+ const upper = method.toUpperCase();
21
+ if (!isHttpMethod(upper)) {
22
+ throw new Error(`Invalid HTTP method: "${method}"`);
23
+ }
24
+ return upper;
25
+ }
package/src/errors.ts CHANGED
@@ -9,7 +9,9 @@ export class SchmockError extends Error {
9
9
  ) {
10
10
  super(message);
11
11
  this.name = "SchmockError";
12
- Error.captureStackTrace(this, this.constructor);
12
+ if (typeof Error.captureStackTrace === "function") {
13
+ Error.captureStackTrace(this, this.constructor);
14
+ }
13
15
  }
14
16
  }
15
17
 
package/src/index.ts CHANGED
@@ -55,6 +55,13 @@ export function schmock(config?: GlobalConfig): CallableMockInstance {
55
55
  return callableInstance as CallableMockInstance;
56
56
  }
57
57
 
58
+ // Re-export constants and utilities
59
+ export {
60
+ HTTP_METHODS,
61
+ isHttpMethod,
62
+ ROUTE_NOT_FOUND_CODE,
63
+ toHttpMethod,
64
+ } from "./constants.js";
58
65
  // Re-export errors
59
66
  export {
60
67
  PluginError,
@@ -81,7 +88,9 @@ export type {
81
88
  RequestContext,
82
89
  RequestOptions,
83
90
  Response,
91
+ ResponseBody,
84
92
  ResponseResult,
85
93
  RouteConfig,
86
94
  RouteKey,
95
+ StaticData,
87
96
  } from "./types.js";
@@ -311,7 +311,7 @@ describe("route matching", () => {
311
311
  mock("GET /items/:id", ({ params }) => ({
312
312
  id: params.id,
313
313
  type: typeof params.id,
314
- parsed: Number.parseInt(params.id),
314
+ parsed: Number.parseInt(params.id, 10),
315
315
  }));
316
316
 
317
317
  const response = await mock.handle("GET", "/items/12345");
@@ -413,6 +413,24 @@ describeFeature(feature, ({ Scenario }) => {
413
413
  });
414
414
  });
415
415
 
416
+ Scenario("Registering duplicate routes first route wins", ({ Given, When, Then }) => {
417
+ Given("I create a mock with duplicate routes:", (_, docString: string) => {
418
+ mock = schmock();
419
+ mock('GET /items', [{ id: 1 }]);
420
+ mock('GET /items', [{ id: 2 }]);
421
+ });
422
+
423
+ When("I request {string}", async (_, request: string) => {
424
+ const [method, path] = request.split(" ");
425
+ response = await mock.handle(method as any, path);
426
+ });
427
+
428
+ Then("the first route response should win:", (_, docString: string) => {
429
+ const expected = JSON.parse(docString);
430
+ expect(response.body).toEqual(expected);
431
+ });
432
+ });
433
+
416
434
  Scenario("Plugin returning unexpected structure", ({ Given, When, Then, And }) => {
417
435
  Given("I create a mock with malformed plugin:", (_, docString: string) => {
418
436
  mock = schmock();
@@ -361,6 +361,31 @@ describeFeature(feature, ({ Scenario }) => {
361
361
  });
362
362
  });
363
363
 
364
+ Scenario("Plugin onError returns response with status 0", ({ Given, When, Then, And }) => {
365
+ Given("I create a mock with status-zero error handler:", (_, docString: string) => {
366
+ mock = schmock();
367
+ const plugin = {
368
+ name: 'zero-status',
369
+ process: () => { throw new Error('fail'); },
370
+ onError: () => ({ status: 0, body: 'zero status', headers: {} })
371
+ };
372
+ mock('GET /zero', 'original').pipe(plugin);
373
+ });
374
+
375
+ When("I request {string}", async (_, request: string) => {
376
+ const [method, path] = request.split(" ");
377
+ response = await mock.handle(method as any, path);
378
+ });
379
+
380
+ Then("I should receive status {int}", (_, status: number) => {
381
+ expect(response.status).toBe(status);
382
+ });
383
+
384
+ And("I should receive text {string}", (_, expectedText: string) => {
385
+ expect(response.body).toBe(expectedText);
386
+ });
387
+ });
388
+
364
389
  Scenario("Plugin null/undefined return handling", ({ Given, When, Then, And }) => {
365
390
  Given("I create a mock with null-returning plugin:", (_, docString: string) => {
366
391
  mock = schmock();
package/src/types.ts CHANGED
@@ -52,7 +52,7 @@ export interface Plugin {
52
52
  */
53
53
  process(
54
54
  context: PluginContext,
55
- response?: any,
55
+ response?: unknown,
56
56
  ): PluginResult | Promise<PluginResult>;
57
57
 
58
58
  /**
@@ -79,7 +79,7 @@ export interface PluginResult {
79
79
  /** Updated context */
80
80
  context: PluginContext;
81
81
  /** Response data (if generated/modified) */
82
- response?: any;
82
+ response?: unknown;
83
83
  }
84
84
 
85
85
  /**
@@ -89,7 +89,7 @@ export interface PluginContext {
89
89
  /** Request path */
90
90
  path: string;
91
91
  /** Matched route configuration */
92
- route: any;
92
+ route: RouteConfig;
93
93
  /** HTTP method */
94
94
  method: HttpMethod;
95
95
  /** Route parameters */
@@ -99,11 +99,11 @@ export interface PluginContext {
99
99
  /** Request headers */
100
100
  headers: Record<string, string>;
101
101
  /** Request body */
102
- body?: any;
102
+ body?: unknown;
103
103
  /** Shared state between plugins for this request */
104
- state: Map<string, any>;
104
+ state: Map<string, unknown>;
105
105
  /** Route-specific state */
106
- routeState?: any;
106
+ routeState?: Record<string, unknown>;
107
107
  }
108
108
 
109
109
  /**
@@ -117,7 +117,7 @@ export interface GlobalConfig {
117
117
  /** Enable debug mode for detailed logging */
118
118
  debug?: boolean;
119
119
  /** Initial shared state object */
120
- state?: any;
120
+ state?: Record<string, unknown>;
121
121
  }
122
122
 
123
123
  /**
@@ -142,10 +142,22 @@ export type GeneratorFunction = (
142
142
  context: RequestContext,
143
143
  ) => ResponseResult | Promise<ResponseResult>;
144
144
 
145
+ /**
146
+ * Response body type alias
147
+ */
148
+ export type ResponseBody = unknown;
149
+
145
150
  /**
146
151
  * Static data (non-function) that gets returned as-is
147
152
  */
148
- export type StaticData = any;
153
+ export type StaticData =
154
+ | string
155
+ | number
156
+ | boolean
157
+ | null
158
+ | undefined
159
+ | Record<string, unknown>
160
+ | unknown[];
149
161
 
150
162
  /**
151
163
  * Context passed to generator functions
@@ -162,9 +174,9 @@ export interface RequestContext {
162
174
  /** Request headers */
163
175
  headers: Record<string, string>;
164
176
  /** Request body (for POST, PUT, PATCH) */
165
- body?: any;
177
+ body?: unknown;
166
178
  /** Shared mutable state */
167
- state: any;
179
+ state: Record<string, unknown>;
168
180
  }
169
181
 
170
182
  /**
@@ -174,16 +186,16 @@ export interface RequestContext {
174
186
  * - [status, body, headers]: custom status, body, and headers
175
187
  */
176
188
  export type ResponseResult =
177
- | any
178
- | [number, any]
179
- | [number, any, Record<string, string>];
189
+ | ResponseBody
190
+ | [number, ResponseBody]
191
+ | [number, ResponseBody, Record<string, string>];
180
192
 
181
193
  /**
182
194
  * Response object returned by handle method
183
195
  */
184
196
  export interface Response {
185
197
  status: number;
186
- body: any;
198
+ body: unknown;
187
199
  headers: Record<string, string>;
188
200
  }
189
201
 
@@ -192,7 +204,7 @@ export interface Response {
192
204
  */
193
205
  export interface RequestOptions {
194
206
  headers?: Record<string, string>;
195
- body?: any;
207
+ body?: unknown;
196
208
  query?: Record<string, string>;
197
209
  }
198
210