@schmock/express 2.1.2 → 2.1.3

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.
Files changed (2) hide show
  1. package/dist/index.js +147 -1
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -1 +1,147 @@
1
- var Q=["GET","POST","PUT","DELETE","PATCH","HEAD","OPTIONS"];function K(V){return Q.includes(V)}function A(V){let $=V.toUpperCase();if(!K($))throw Error(`Invalid HTTP method: "${V}"`);return $}function z(V){let{status:$,body:J}=V;return $===404&&J!==null&&typeof J==="object"&&"code"in J&&J.code==="ROUTE_NOT_FOUND"}class W extends Error{code;context;constructor(V,$,J){super(V);this.code=$;this.context=J;if(this.name="SchmockError",typeof Error.captureStackTrace==="function")Error.captureStackTrace(this,this.constructor)}}function k(V,$){if(V.status!=null)$.status(V.status);if(V.headers)Object.entries(V.headers).forEach(([J,X])=>{if(typeof X==="string")$.set(J,X)});if(V.body!==void 0)if(typeof V.body==="string")$.send(V.body);else $.json(V.body);else $.end()}function v(V){return Object.fromEntries(Object.entries(V).map(([$,J])=>[$,Array.isArray(J)?J[0]:J]).filter(($)=>typeof $[1]==="string"))}function q(V){let $={};for(let[J,X]of Object.entries(V))if(typeof X==="string")$[J]=X;else if(Array.isArray(X))$[J]=X[0]?String(X[0]):"";else if(X!=null)$[J]=String(X);return $}function q0(V,$={}){let{errorFormatter:J,passErrorsToNext:X=!0,transformHeaders:T=v,transformQuery:j=q,beforeRequest:B,beforeResponse:N}=$;return async(L,O,U)=>{try{let Z;try{Z=A(L.method)}catch{return U()}let G={method:Z,path:L.path,headers:T(L.headers),body:L.body,query:j(L.query)};if(B){let _=await B(L,O);if(_)G={...G,..._,method:A(_.method||G.method)}}let Y=await V.handle(G.method,G.path,{headers:G.headers,body:G.body,query:G.query});if(z(Y)){U();return}if(N){let _=await N(Y,L,O);if(_)Y=_}if(J&&Y.status===500&&Y.body!==null&&typeof Y.body==="object"&&"error"in Y.body&&"code"in Y.body){let _=Y.body,F=typeof _.error==="string"?_.error:"Unknown error",M=J(Error(F),L);O.status(500).json(M);return}k(Y,O)}catch(Z){if(J){let G=Z instanceof Error?Z:Error(String(Z)),Y=J(G,L);O.status(500).json(Y)}else if(X)U(Z);else O.status(500).json({error:Z instanceof Error?Z.message:"Internal Server Error",code:Z instanceof W?Z.code:"INTERNAL_ERROR"})}}}export{q0 as toExpress};
1
+ /// <reference path="../../core/schmock.d.ts" />
2
+ import { isRouteNotFound, SchmockError, toHttpMethod } from "@schmock/core";
3
+ /**
4
+ * Convert Schmock response to Express response
5
+ */
6
+ function schmockToExpressResponse(schmockResponse, res) {
7
+ // Set status code
8
+ if (schmockResponse.status != null) {
9
+ res.status(schmockResponse.status);
10
+ }
11
+ // Set headers
12
+ if (schmockResponse.headers) {
13
+ Object.entries(schmockResponse.headers).forEach(([key, value]) => {
14
+ if (typeof value === "string") {
15
+ res.set(key, value);
16
+ }
17
+ });
18
+ }
19
+ // Send body
20
+ if (schmockResponse.body !== undefined) {
21
+ if (typeof schmockResponse.body === "string") {
22
+ res.send(schmockResponse.body);
23
+ }
24
+ else {
25
+ res.json(schmockResponse.body);
26
+ }
27
+ }
28
+ else {
29
+ res.end();
30
+ }
31
+ }
32
+ /**
33
+ * Default header transformer
34
+ */
35
+ function defaultTransformHeaders(headers) {
36
+ return Object.fromEntries(Object.entries(headers)
37
+ .map(([key, value]) => [key, Array.isArray(value) ? value[0] : value])
38
+ .filter((entry) => typeof entry[1] === "string"));
39
+ }
40
+ /**
41
+ * Default query transformer
42
+ */
43
+ function defaultTransformQuery(query) {
44
+ const result = {};
45
+ for (const [key, value] of Object.entries(query)) {
46
+ if (typeof value === "string") {
47
+ result[key] = value;
48
+ }
49
+ else if (Array.isArray(value)) {
50
+ result[key] = value[0] ? String(value[0]) : "";
51
+ }
52
+ else if (value != null) {
53
+ result[key] = String(value);
54
+ }
55
+ }
56
+ return result;
57
+ }
58
+ /**
59
+ * Convert a Schmock mock instance to Express middleware
60
+ */
61
+ export function toExpress(mock, options = {}) {
62
+ const { errorFormatter, passErrorsToNext = true, transformHeaders = defaultTransformHeaders, transformQuery = defaultTransformQuery, beforeRequest, beforeResponse, } = options;
63
+ return async (req, res, next) => {
64
+ try {
65
+ // Skip non-standard HTTP methods (e.g. WebDAV PROPFIND, LOCK)
66
+ let method;
67
+ try {
68
+ method = toHttpMethod(req.method);
69
+ }
70
+ catch {
71
+ return next();
72
+ }
73
+ // Run request interceptor if provided
74
+ let requestData = {
75
+ method,
76
+ path: req.path,
77
+ headers: transformHeaders(req.headers),
78
+ body: req.body,
79
+ query: transformQuery(req.query),
80
+ };
81
+ if (beforeRequest) {
82
+ const intercepted = await beforeRequest(req, res);
83
+ if (intercepted) {
84
+ requestData = {
85
+ ...requestData,
86
+ ...intercepted,
87
+ method: toHttpMethod(intercepted.method || requestData.method),
88
+ };
89
+ }
90
+ }
91
+ // Handle request with Schmock
92
+ let schmockResponse = await mock.handle(requestData.method, requestData.path, {
93
+ headers: requestData.headers,
94
+ body: requestData.body,
95
+ query: requestData.query,
96
+ });
97
+ // Detect ROUTE_NOT_FOUND responses and pass to next middleware
98
+ if (isRouteNotFound(schmockResponse)) {
99
+ next();
100
+ return;
101
+ }
102
+ // Run response interceptor if provided
103
+ if (beforeResponse) {
104
+ const intercepted = await beforeResponse(schmockResponse, req, res);
105
+ if (intercepted) {
106
+ schmockResponse = intercepted;
107
+ }
108
+ }
109
+ // Auto-route schmock's internal 500 ({ error, code }) through
110
+ // errorFormatter so the formatter sees every error path, not just
111
+ // those caught here. Mirrors the Angular adapter behaviour.
112
+ if (errorFormatter &&
113
+ schmockResponse.status === 500 &&
114
+ schmockResponse.body !== null &&
115
+ typeof schmockResponse.body === "object" &&
116
+ "error" in schmockResponse.body &&
117
+ "code" in schmockResponse.body) {
118
+ const respBody = schmockResponse.body;
119
+ const errMsg = typeof respBody.error === "string" ? respBody.error : "Unknown error";
120
+ const formatted = errorFormatter(new Error(errMsg), req);
121
+ res.status(500).json(formatted);
122
+ return;
123
+ }
124
+ // Convert and send Schmock response
125
+ schmockToExpressResponse(schmockResponse, res);
126
+ }
127
+ catch (error) {
128
+ // Handle errors based on configuration
129
+ if (errorFormatter) {
130
+ // Fires for any Error from the handler/pipeline, not just
131
+ // SchmockError — matches the Angular adapter's behavior.
132
+ const err = error instanceof Error ? error : new Error(String(error));
133
+ const formatted = errorFormatter(err, req);
134
+ res.status(500).json(formatted);
135
+ }
136
+ else if (passErrorsToNext) {
137
+ next(error);
138
+ }
139
+ else {
140
+ res.status(500).json({
141
+ error: error instanceof Error ? error.message : "Internal Server Error",
142
+ code: error instanceof SchmockError ? error.code : "INTERNAL_ERROR",
143
+ });
144
+ }
145
+ }
146
+ };
147
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@schmock/express",
3
- "version": "2.1.2",
3
+ "version": "2.1.3",
4
4
  "description": "Express adapter for Schmock mock API generator",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -33,10 +33,10 @@
33
33
  "@types/node": "^25.8.0",
34
34
  "express": "^5.2.1",
35
35
  "typescript": "^6.0.3",
36
- "vitest": "^4.1.6"
36
+ "vitest": "^4.1.7"
37
37
  },
38
38
  "peerDependencies": {
39
- "@schmock/core": "^2.1.2",
39
+ "@schmock/core": "^2.1.3",
40
40
  "express": "^4.18.0 || ^5.0.0"
41
41
  },
42
42
  "keywords": [