@jaredwray/mockhttp 0.3.0 → 0.4.1

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/README.md CHANGED
@@ -11,8 +11,8 @@ A simple HTTP server that can be used to mock HTTP responses for testing purpose
11
11
 
12
12
  # Features
13
13
  * All the features of [httpbin](https://httpbin.org/)
14
- * CORS and Helmet support
15
- * Built with `nodejs` and `fastify`
14
+ * `@fastify/helmet` built in by default
15
+ * Built with `nodejs`, `typescript`, and `fastify`
16
16
  * Deploy via `docker` or `nodejs`
17
17
  * Global deployment via [mockhttp.org](https://mockhttp.org) (free service)
18
18
  * Better API documentation and examples
@@ -33,13 +33,13 @@ then run `mockhttp` in your code.
33
33
  ```javascript
34
34
  import { mockhttp } from '@jaredwray/mockhttp';
35
35
  await mockhttp.start(); // start the server
36
- const response = await fetch('http://localhost:8080/ip');
36
+ const response = await fetch('http://localhost:3000/get');
37
37
  await mockhttp.stop(); // stop the server
38
38
  ```
39
39
 
40
40
  # About mockhttp.org
41
41
 
42
- [mockhttp.org](https://mockhttp.org) is a free service that runs this codebase and allows you to use it for testing purposes. It is a simple way to mock HTTP responses for testing purposes. It is globally available has some limitations on it to prevent abuse but many people will not see it. It is ran via [Cloudflare](https://cloudflare.com) and [Google Cloud Run](https://cloud.google.com/run/) across 7 regions globally and can do millions of requests per second.
42
+ [mockhttp.org](https://mockhttp.org) is a free service that runs this codebase and allows you to use it for testing purposes. It is a simple way to mock HTTP responses for testing purposes. It is globally available has some limitations on it to prevent abuse such as requests per second. It is ran via [Cloudflare](https://cloudflare.com) and [Google Cloud Run](https://cloud.google.com/run/) across 7 regions globally and can do millions of requests per second.
43
43
 
44
44
  # Contributing
45
45
 
package/dist/index.d.ts CHANGED
@@ -1,3 +1,69 @@
1
- declare const start: () => Promise<void>;
1
+ import { Hookified, HookifiedOptions } from 'hookified';
2
+ import { FastifyInstance } from 'fastify';
2
3
 
3
- export { start };
4
+ type HttpBinOptions = {
5
+ httpMethods?: boolean;
6
+ redirects?: boolean;
7
+ requestInspection?: boolean;
8
+ responseInspection?: boolean;
9
+ statusCodes?: boolean;
10
+ };
11
+ type MockHttpOptions = {
12
+ /**
13
+ * The port to listen on. If not provided, defaults to 3000 and falls back to 3001 if the PORT is in use.
14
+ */
15
+ port?: number;
16
+ /**
17
+ * The host to listen on. If not provided, defaults to '0.0.0.0'.
18
+ */
19
+ host?: string;
20
+ /**
21
+ * Whether to use Helmet for security headers. Defaults to true.
22
+ */
23
+ helmet?: boolean;
24
+ /**
25
+ * Whether to use Swagger for API documentation. Defaults to true.
26
+ */
27
+ apiDocs?: boolean;
28
+ /**
29
+ * Whether to use Swagger UI. Defaults to true.
30
+ */
31
+ httpBin?: HttpBinOptions;
32
+ /**
33
+ * Hookified options.
34
+ */
35
+ hookOptions?: HookifiedOptions;
36
+ };
37
+ declare class MockHttp extends Hookified {
38
+ private _port;
39
+ private _host;
40
+ private _helmet;
41
+ private _apiDocs;
42
+ private _httpBin;
43
+ private _server;
44
+ constructor(options?: MockHttpOptions);
45
+ get port(): number;
46
+ set port(port: number);
47
+ get host(): string;
48
+ set host(host: string);
49
+ get helmet(): boolean;
50
+ set helmet(helmet: boolean);
51
+ get apiDocs(): boolean;
52
+ set apiDocs(apiDocs: boolean);
53
+ get httpBin(): HttpBinOptions;
54
+ set httpBin(httpBinary: HttpBinOptions);
55
+ get server(): FastifyInstance;
56
+ set server(server: FastifyInstance);
57
+ start(): Promise<void>;
58
+ close(): Promise<void>;
59
+ registerApiDocs(fastifyInstance?: FastifyInstance): Promise<void>;
60
+ registerHttpMethods(fastifyInstance?: FastifyInstance): Promise<void>;
61
+ registerStatusCodeRoutes(fastifyInstance?: FastifyInstance): Promise<void>;
62
+ registerRequestInspectionRoutes(fastifyInstance?: FastifyInstance): Promise<void>;
63
+ registerResponseInspectionRoutes(fastifyInstance?: FastifyInstance): Promise<void>;
64
+ registerRedirectRoutes(fastifyInstance?: FastifyInstance): Promise<void>;
65
+ }
66
+
67
+ declare const start: () => Promise<MockHttp>;
68
+
69
+ export { MockHttp as default, MockHttp as mockhttp, start };
package/dist/index.js CHANGED
@@ -3,128 +3,14 @@ var __name = (target, value) => __defProp(target, "name", { value, configurable:
3
3
 
4
4
  // src/index.ts
5
5
  import process from "node:process";
6
+
7
+ // src/mock-http.ts
6
8
  import path from "node:path";
7
- import Fastify from "fastify";
8
9
  import fastifyStatic from "@fastify/static";
9
- import { fastifySwagger } from "@fastify/swagger";
10
10
  import fastifyHelmet from "@fastify/helmet";
11
-
12
- // src/routes/index.ts
13
- var indexRoute = /* @__PURE__ */ __name((fastify2) => {
14
- fastify2.get("/", {
15
- schema: {
16
- hide: true
17
- }
18
- }, async (_request, reply) => {
19
- const redocHtml = `
20
- <!doctype html>
21
- <html>
22
- <head>
23
- <title>{ mockhttp }</title>
24
- <meta charset="utf-8" />
25
- <meta
26
- name="viewport"
27
- content="width=device-width, initial-scale=1" />
28
- <style>
29
- :root {
30
- --scalar-custom-header-height: 70px;
31
- }
32
- .custom-header {
33
- height: var(--scalar-custom-header-height);
34
- background-color: var(--scalar-background-1);
35
- box-shadow: inset 0 -1px 0 var(--scalar-border-color);
36
- color: var(--scalar-color-1);
37
- font-size: var(--scalar-font-size-2);
38
- padding: 0 18px;
39
- position: sticky;
40
- justify-content: space-between;
41
- top: 0;
42
- z-index: 100;
43
- }
44
- .custom-header,
45
- .custom-header nav {
46
- display: flex;
47
- align-items: center;
48
- gap: 18px;
49
- }
50
-
51
- .custom-header img {
52
- height: var(--scalar-custom-header-height);
53
- object-fit: contain;
54
- }
55
-
56
- .custom-header a:hover {
57
- color: var(--scalar-color-2);
58
- }
59
-
60
- .github-corner {
61
- fill: var(--scalar-border-color) !important;
62
- }
63
-
64
- </style>
65
- </head>
66
- <body>
67
- <header class="custom-header scalar-app">
68
- <a href="/"><img src="/logo.svg" alt="{ mockhttp }" /></a>
69
- <a href="https://github.com/jaredwray/mockhttp" class="github-corner" aria-label="View source on GitHub">
70
- <svg width="70" height="70" viewBox="0 0 250 250" style="color:#fff; position: absolute; top: 0; border: 0; right: 0;" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a><style>.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}</style>
71
- </header>
72
-
73
- <script
74
- id="api-reference"
75
- data-url="/docs/json"></script>
76
-
77
- <script src="/scalar/browser/standalone.js"></script>
78
- </body>
79
- </html>
80
- `;
81
- await reply.type("text/html").send(redocHtml);
82
- });
83
- }, "indexRoute");
84
-
85
- // src/routes/sitemap.ts
86
- var sitemapRoute = /* @__PURE__ */ __name((fastify2) => {
87
- fastify2.get("/sitemap.xml", {
88
- schema: {
89
- hide: true
90
- }
91
- }, async (_request, reply) => {
92
- try {
93
- const host = _request.headers.host ?? "mockhttp.org";
94
- const isSecure = _request.headers["x-forwarded-proto"]?.includes("https");
95
- const protocol = isSecure ? "https" : "http";
96
- const xml = `<?xml version="1.0" encoding="UTF-8"?>
97
- <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
98
- <url>
99
- <loc>${protocol}://${host}/</loc>
100
- <changefreq>monthly</changefreq>
101
- <priority>1.0</priority>
102
- </url>
103
- </urlset>`;
104
- await reply.type("text/xml").send(xml);
105
- } catch (error) {
106
- fastify2.log.error(error);
107
- await reply.status(500).send({
108
- error: "Internal Server Error"
109
- });
110
- }
111
- });
112
- }, "sitemapRoute");
113
-
114
- // src/fastify-config.ts
115
- var fastifyConfig = {
116
- logger: {
117
- transport: {
118
- target: "pino-pretty",
119
- options: {
120
- colorize: true,
121
- translateTime: true,
122
- ignore: "pid,hostname",
123
- singleLine: true
124
- }
125
- }
126
- }
127
- };
11
+ import { fastifySwagger } from "@fastify/swagger";
12
+ import { Hookified } from "hookified";
13
+ import Fastify from "fastify";
128
14
 
129
15
  // src/swagger.ts
130
16
  import { fastifySwaggerUi } from "@fastify/swagger-ui";
@@ -132,7 +18,7 @@ import { fastifySwaggerUi } from "@fastify/swagger-ui";
132
18
  // package.json
133
19
  var package_default = {
134
20
  name: "@jaredwray/mockhttp",
135
- version: "0.3.0",
21
+ version: "0.4.1",
136
22
  description: "Mock Http - Easy way to mock http with httpbin replacement",
137
23
  type: "module",
138
24
  main: "dist/index.js",
@@ -168,23 +54,24 @@ var package_default = {
168
54
  },
169
55
  homepage: "https://github.com/jaredwray/mockhttp#readme",
170
56
  devDependencies: {
171
- "@swc/core": "^1.10.9",
172
- "@types/html-escaper": "^3.0.2",
173
- "@vitest/coverage-v8": "^3.0.4",
57
+ "@swc/core": "^1.11.5",
58
+ "@types/html-escaper": "^3.0.4",
59
+ "@vitest/coverage-v8": "^3.0.7",
174
60
  rimraf: "^6.0.1",
175
- tsup: "^8.3.6",
176
- tsx: "^4.19.2",
177
- typescript: "^5.7.3",
178
- vitest: "^3.0.4",
61
+ tsup: "^8.4.0",
62
+ tsx: "^4.19.3",
63
+ typescript: "^5.8.2",
64
+ vitest: "^3.0.7",
179
65
  xo: "^0.60.0"
180
66
  },
181
67
  dependencies: {
182
68
  "@fastify/helmet": "^13.0.1",
183
- "@fastify/static": "^8.0.4",
69
+ "@fastify/static": "^8.1.1",
184
70
  "@fastify/swagger": "^9.4.2",
185
- "@fastify/swagger-ui": "^5.2.1",
186
- "@scalar/api-reference": "^1.25.108",
71
+ "@fastify/swagger-ui": "^5.2.2",
72
+ "@scalar/api-reference": "^1.25.124",
187
73
  fastify: "^5.2.1",
74
+ hookified: "^1.7.1",
188
75
  "html-escaper": "^3.0.3",
189
76
  pino: "^9.6.0",
190
77
  "pino-pretty": "^13.0.0"
@@ -193,7 +80,14 @@ var package_default = {
193
80
  "dist",
194
81
  "public",
195
82
  "LICENCE"
196
- ]
83
+ ],
84
+ pnpm: {
85
+ onlyBuiltDependencies: [
86
+ "@swc/core",
87
+ "esbuild",
88
+ "vue-demi"
89
+ ]
90
+ }
197
91
  };
198
92
 
199
93
  // src/swagger.ts
@@ -223,8 +117,8 @@ var fastifySwaggerConfig = {
223
117
  ]
224
118
  }
225
119
  };
226
- var registerSwaggerUi = /* @__PURE__ */ __name(async (fastify2) => {
227
- await fastify2.register(fastifySwaggerUi, {
120
+ var registerSwaggerUi = /* @__PURE__ */ __name(async (fastify) => {
121
+ await fastify.register(fastifySwaggerUi, {
228
122
  routePrefix: "/docs",
229
123
  uiConfig: {
230
124
  docExpansion: "none",
@@ -246,110 +140,122 @@ var registerSwaggerUi = /* @__PURE__ */ __name(async (fastify2) => {
246
140
  });
247
141
  }, "registerSwaggerUi");
248
142
 
249
- // src/routes/request-inspection/ip.ts
250
- var ipSchema = {
251
- description: "Returns the IP address of the client",
252
- tags: [
253
- "Request Inspection"
254
- ],
255
- response: {
256
- // eslint-disable-next-line @typescript-eslint/naming-convention
257
- 200: {
258
- type: "object",
259
- properties: {
260
- ip: {
261
- type: "string",
262
- description: "The IP address of the client"
263
- }
264
- },
265
- required: [
266
- "ip"
267
- ]
143
+ // src/fastify-config.ts
144
+ var fastifyConfig = {
145
+ logger: {
146
+ transport: {
147
+ target: "pino-pretty",
148
+ options: {
149
+ colorize: true,
150
+ translateTime: true,
151
+ ignore: "pid,hostname",
152
+ singleLine: true
153
+ }
268
154
  }
269
155
  }
270
156
  };
271
- var ipRoute = /* @__PURE__ */ __name((fastify2) => {
272
- fastify2.get("/ip", {
273
- schema: ipSchema
274
- }, async (request) => {
275
- const cloudflareIp = request.headers["cf-connecting-ip"];
276
- const forwardedIp = request.headers["x-forwarded-for"];
277
- const remoteIp = request.ip;
278
- const ip = cloudflareIp ?? (typeof forwardedIp === "string" ? forwardedIp.split(",")[0].trim() : remoteIp);
279
- return {
280
- ip
281
- };
282
- });
283
- }, "ipRoute");
284
157
 
285
- // src/routes/request-inspection/headers.ts
286
- var headersSchema = {
287
- description: "Returns all headers of the client request",
288
- tags: [
289
- "Request Inspection"
290
- ],
291
- response: {
292
- // eslint-disable-next-line @typescript-eslint/naming-convention
293
- 200: {
294
- type: "object",
295
- properties: {
296
- headers: {
297
- type: "object",
298
- additionalProperties: {
299
- type: "string"
300
- },
301
- description: "All headers of the client request"
302
- }
303
- },
304
- required: [
305
- "headers"
306
- ]
158
+ // src/routes/index.ts
159
+ var indexRoute = /* @__PURE__ */ __name((fastify) => {
160
+ fastify.get("/", {
161
+ schema: {
162
+ hide: true
307
163
  }
308
- }
309
- };
310
- var headersRoute = /* @__PURE__ */ __name((fastify2) => {
311
- fastify2.get("/headers", {
312
- schema: headersSchema
313
- }, async (request) => {
314
- const headers = request.headers;
315
- return {
316
- headers
317
- };
164
+ }, async (_request, reply) => {
165
+ const redocHtml = `
166
+ <!doctype html>
167
+ <html>
168
+ <head>
169
+ <title>{ mockhttp }</title>
170
+ <meta charset="utf-8" />
171
+ <meta
172
+ name="viewport"
173
+ content="width=device-width, initial-scale=1" />
174
+ <style>
175
+ :root {
176
+ --scalar-custom-header-height: 70px;
177
+ }
178
+ .custom-header {
179
+ height: var(--scalar-custom-header-height);
180
+ background-color: var(--scalar-background-1);
181
+ box-shadow: inset 0 -1px 0 var(--scalar-border-color);
182
+ color: var(--scalar-color-1);
183
+ font-size: var(--scalar-font-size-2);
184
+ padding: 0 18px;
185
+ position: sticky;
186
+ justify-content: space-between;
187
+ top: 0;
188
+ z-index: 100;
189
+ }
190
+ .custom-header,
191
+ .custom-header nav {
192
+ display: flex;
193
+ align-items: center;
194
+ gap: 18px;
195
+ }
196
+
197
+ .custom-header img {
198
+ height: var(--scalar-custom-header-height);
199
+ object-fit: contain;
200
+ }
201
+
202
+ .custom-header a:hover {
203
+ color: var(--scalar-color-2);
204
+ }
205
+
206
+ .github-corner {
207
+ fill: var(--scalar-border-color) !important;
208
+ }
209
+
210
+ </style>
211
+ </head>
212
+ <body>
213
+ <header class="custom-header scalar-app">
214
+ <a href="/"><img src="/logo.svg" alt="{ mockhttp }" /></a>
215
+ <a href="https://github.com/jaredwray/mockhttp" class="github-corner" aria-label="View source on GitHub">
216
+ <svg width="70" height="70" viewBox="0 0 250 250" style="color:#fff; position: absolute; top: 0; border: 0; right: 0;" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a><style>.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}</style>
217
+ </header>
218
+
219
+ <script
220
+ id="api-reference"
221
+ data-url="/docs/json"></script>
222
+
223
+ <script src="/scalar/browser/standalone.js"></script>
224
+ </body>
225
+ </html>
226
+ `;
227
+ await reply.type("text/html").send(redocHtml);
318
228
  });
319
- }, "headersRoute");
229
+ }, "indexRoute");
320
230
 
321
- // src/routes/request-inspection/user-agent.ts
322
- var userAgentSchema = {
323
- description: "Returns the User-Agent header of the client request",
324
- tags: [
325
- "Request Inspection"
326
- ],
327
- response: {
328
- // eslint-disable-next-line @typescript-eslint/naming-convention
329
- 200: {
330
- type: "object",
331
- properties: {
332
- userAgent: {
333
- type: "string",
334
- description: "The User-Agent header of the client request"
335
- }
336
- },
337
- required: [
338
- "userAgent"
339
- ]
231
+ // src/routes/sitemap.ts
232
+ var sitemapRoute = /* @__PURE__ */ __name((fastify) => {
233
+ fastify.get("/sitemap.xml", {
234
+ schema: {
235
+ hide: true
236
+ }
237
+ }, async (_request, reply) => {
238
+ try {
239
+ const host = _request.headers.host ?? "mockhttp.org";
240
+ const isSecure = _request.headers["x-forwarded-proto"]?.includes("https");
241
+ const protocol = isSecure ? "https" : "http";
242
+ const xml = `<?xml version="1.0" encoding="UTF-8"?>
243
+ <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
244
+ <url>
245
+ <loc>${protocol}://${host}/</loc>
246
+ <changefreq>monthly</changefreq>
247
+ <priority>1.0</priority>
248
+ </url>
249
+ </urlset>`;
250
+ await reply.type("text/xml").send(xml);
251
+ } catch (error) {
252
+ fastify.log.error(error);
253
+ await reply.status(500).send({
254
+ error: "Internal Server Error"
255
+ });
340
256
  }
341
- }
342
- };
343
- var userAgentRoute = /* @__PURE__ */ __name((fastify2) => {
344
- fastify2.get("/user-agent", {
345
- schema: userAgentSchema
346
- }, async (request) => {
347
- const userAgent = request.headers["user-agent"];
348
- return {
349
- userAgent
350
- };
351
257
  });
352
- }, "userAgentRoute");
258
+ }, "sitemapRoute");
353
259
 
354
260
  // src/routes/http-methods/get.ts
355
261
  var getSchema = {
@@ -398,8 +304,8 @@ var getSchema = {
398
304
  }
399
305
  }
400
306
  };
401
- var getRoute = /* @__PURE__ */ __name((fastify2) => {
402
- fastify2.get("/get", {
307
+ var getRoute = /* @__PURE__ */ __name((fastify) => {
308
+ fastify.get("/get", {
403
309
  schema: getSchema
404
310
  }, async (request, _reply) => ({
405
311
  method: "GET",
@@ -446,8 +352,8 @@ var postSchema = {
446
352
  }
447
353
  }
448
354
  };
449
- var postRoute = /* @__PURE__ */ __name((fastify2) => {
450
- fastify2.post("/post", {
355
+ var postRoute = /* @__PURE__ */ __name((fastify) => {
356
+ fastify.post("/post", {
451
357
  schema: postSchema
452
358
  }, async (request) => ({
453
359
  method: "POST",
@@ -494,8 +400,8 @@ var deleteSchema = {
494
400
  }
495
401
  }
496
402
  };
497
- var deleteRoute = /* @__PURE__ */ __name((fastify2) => {
498
- fastify2.delete("/delete", {
403
+ var deleteRoute = /* @__PURE__ */ __name((fastify) => {
404
+ fastify.delete("/delete", {
499
405
  schema: deleteSchema
500
406
  }, async (request) => ({
501
407
  method: "DELETE",
@@ -542,8 +448,8 @@ var putSchema = {
542
448
  }
543
449
  }
544
450
  };
545
- var putRoute = /* @__PURE__ */ __name((fastify2) => {
546
- fastify2.put("/put", {
451
+ var putRoute = /* @__PURE__ */ __name((fastify) => {
452
+ fastify.put("/put", {
547
453
  schema: putSchema
548
454
  }, async (request) => ({
549
455
  method: "PUT",
@@ -590,8 +496,8 @@ var patchSchema = {
590
496
  }
591
497
  }
592
498
  };
593
- var patchRoute = /* @__PURE__ */ __name((fastify2) => {
594
- fastify2.patch("/patch", {
499
+ var patchRoute = /* @__PURE__ */ __name((fastify) => {
500
+ fastify.patch("/patch", {
595
501
  schema: patchSchema
596
502
  }, async (request) => ({
597
503
  method: "PATCH",
@@ -882,7 +788,7 @@ var statusSchema = {
882
788
  }
883
789
  }
884
790
  };
885
- var statusCodeRoute = /* @__PURE__ */ __name((fastify2) => {
791
+ var statusCodeRoute = /* @__PURE__ */ __name((fastify) => {
886
792
  const handler = /* @__PURE__ */ __name(async (request, reply) => {
887
793
  let { code } = request.params;
888
794
  if (!code || !statusCodes.map((status) => status.code).includes(Number(code))) {
@@ -892,23 +798,128 @@ var statusCodeRoute = /* @__PURE__ */ __name((fastify2) => {
892
798
  status: `Response with status code ${code}`
893
799
  });
894
800
  }, "handler");
895
- fastify2.get("/status/:code?", {
801
+ fastify.get("/status/:code?", {
896
802
  schema: statusSchema
897
803
  }, handler);
898
- fastify2.post("/status/:code?", {
804
+ fastify.post("/status/:code?", {
899
805
  schema: statusSchema
900
806
  }, handler);
901
- fastify2.put("/status/:code?", {
807
+ fastify.put("/status/:code?", {
902
808
  schema: statusSchema
903
809
  }, handler);
904
- fastify2.patch("/status/:code?", {
810
+ fastify.patch("/status/:code?", {
905
811
  schema: statusSchema
906
812
  }, handler);
907
- fastify2.delete("/status/:code?", {
813
+ fastify.delete("/status/:code?", {
908
814
  schema: statusSchema
909
815
  }, handler);
910
816
  }, "statusCodeRoute");
911
817
 
818
+ // src/routes/request-inspection/ip.ts
819
+ var ipSchema = {
820
+ description: "Returns the IP address of the client",
821
+ tags: [
822
+ "Request Inspection"
823
+ ],
824
+ response: {
825
+ // eslint-disable-next-line @typescript-eslint/naming-convention
826
+ 200: {
827
+ type: "object",
828
+ properties: {
829
+ ip: {
830
+ type: "string",
831
+ description: "The IP address of the client"
832
+ }
833
+ },
834
+ required: [
835
+ "ip"
836
+ ]
837
+ }
838
+ }
839
+ };
840
+ var ipRoute = /* @__PURE__ */ __name((fastify) => {
841
+ fastify.get("/ip", {
842
+ schema: ipSchema
843
+ }, async (request) => {
844
+ const cloudflareIp = request.headers["cf-connecting-ip"];
845
+ const forwardedIp = request.headers["x-forwarded-for"];
846
+ const remoteIp = request.ip;
847
+ const ip = cloudflareIp ?? (typeof forwardedIp === "string" ? forwardedIp.split(",")[0].trim() : remoteIp);
848
+ return {
849
+ ip
850
+ };
851
+ });
852
+ }, "ipRoute");
853
+
854
+ // src/routes/request-inspection/headers.ts
855
+ var headersSchema = {
856
+ description: "Returns all headers of the client request",
857
+ tags: [
858
+ "Request Inspection"
859
+ ],
860
+ response: {
861
+ // eslint-disable-next-line @typescript-eslint/naming-convention
862
+ 200: {
863
+ type: "object",
864
+ properties: {
865
+ headers: {
866
+ type: "object",
867
+ additionalProperties: {
868
+ type: "string"
869
+ },
870
+ description: "All headers of the client request"
871
+ }
872
+ },
873
+ required: [
874
+ "headers"
875
+ ]
876
+ }
877
+ }
878
+ };
879
+ var headersRoute = /* @__PURE__ */ __name((fastify) => {
880
+ fastify.get("/headers", {
881
+ schema: headersSchema
882
+ }, async (request) => {
883
+ const headers = request.headers;
884
+ return {
885
+ headers
886
+ };
887
+ });
888
+ }, "headersRoute");
889
+
890
+ // src/routes/request-inspection/user-agent.ts
891
+ var userAgentSchema = {
892
+ description: "Returns the User-Agent header of the client request",
893
+ tags: [
894
+ "Request Inspection"
895
+ ],
896
+ response: {
897
+ // eslint-disable-next-line @typescript-eslint/naming-convention
898
+ 200: {
899
+ type: "object",
900
+ properties: {
901
+ userAgent: {
902
+ type: "string",
903
+ description: "The User-Agent header of the client request"
904
+ }
905
+ },
906
+ required: [
907
+ "userAgent"
908
+ ]
909
+ }
910
+ }
911
+ };
912
+ var userAgentRoute = /* @__PURE__ */ __name((fastify) => {
913
+ fastify.get("/user-agent", {
914
+ schema: userAgentSchema
915
+ }, async (request) => {
916
+ const userAgent = request.headers["user-agent"];
917
+ return {
918
+ userAgent
919
+ };
920
+ });
921
+ }, "userAgentRoute");
922
+
912
923
  // src/routes/response-inspection/cache.ts
913
924
  var cacheGetSchema = {
914
925
  description: "Handles cache validation and retrieval.",
@@ -953,8 +964,8 @@ var cacheSetSchema = {
953
964
  }
954
965
  }
955
966
  };
956
- var cacheRoutes = /* @__PURE__ */ __name((fastify2) => {
957
- fastify2.get("/cache", {
967
+ var cacheRoutes = /* @__PURE__ */ __name((fastify) => {
968
+ fastify.get("/cache", {
958
969
  schema: cacheGetSchema
959
970
  }, async (request, reply) => {
960
971
  const ifModifiedSince = request.headers["if-modified-since"];
@@ -965,7 +976,7 @@ var cacheRoutes = /* @__PURE__ */ __name((fastify2) => {
965
976
  }
966
977
  await reply.send("Cache content or resource response here.");
967
978
  });
968
- fastify2.get("/cache/:value", {
979
+ fastify.get("/cache/:value", {
969
980
  schema: cacheSetSchema
970
981
  }, async (request, reply) => {
971
982
  const { value } = request.params;
@@ -1009,8 +1020,8 @@ var etagSchema = {
1009
1020
  }
1010
1021
  }
1011
1022
  };
1012
- var etagRoutes = /* @__PURE__ */ __name((fastify2) => {
1013
- fastify2.get("/etag/:etag", {
1023
+ var etagRoutes = /* @__PURE__ */ __name((fastify) => {
1024
+ fastify.get("/etag/:etag", {
1014
1025
  schema: etagSchema
1015
1026
  }, async (request, reply) => {
1016
1027
  const { etag } = request.params;
@@ -1049,8 +1060,8 @@ var responseHeadersSchema = {
1049
1060
  }
1050
1061
  }
1051
1062
  };
1052
- var responseHeadersRoutes = /* @__PURE__ */ __name((fastify2) => {
1053
- fastify2.get("/response-headers", {
1063
+ var responseHeadersRoutes = /* @__PURE__ */ __name((fastify) => {
1064
+ fastify.get("/response-headers", {
1054
1065
  schema: responseHeadersSchema
1055
1066
  }, async (request, reply) => {
1056
1067
  const queryParameters = request.query;
@@ -1063,7 +1074,7 @@ var responseHeadersRoutes = /* @__PURE__ */ __name((fastify2) => {
1063
1074
  ]));
1064
1075
  await reply.send(cleanedResponse);
1065
1076
  });
1066
- fastify2.post("/response-headers", {
1077
+ fastify.post("/response-headers", {
1067
1078
  schema: responseHeadersSchema
1068
1079
  }, async (request, reply) => {
1069
1080
  const queryParameters = request.query ?? {};
@@ -1121,8 +1132,8 @@ var absoluteRedirectSchema = {
1121
1132
  }
1122
1133
  }
1123
1134
  };
1124
- var absoluteRedirectRoute = /* @__PURE__ */ __name((fastify2) => {
1125
- fastify2.get("/absolute-redirect/:value", {
1135
+ var absoluteRedirectRoute = /* @__PURE__ */ __name((fastify) => {
1136
+ fastify.get("/absolute-redirect/:value", {
1126
1137
  schema: absoluteRedirectSchema
1127
1138
  }, async (request, reply) => {
1128
1139
  const value = request.params.value;
@@ -1182,8 +1193,8 @@ var relativeRedirectSchema = {
1182
1193
  }
1183
1194
  }
1184
1195
  };
1185
- var relativeRedirectRoute = /* @__PURE__ */ __name((fastify2) => {
1186
- fastify2.get("/relative-redirect/:value", {
1196
+ var relativeRedirectRoute = /* @__PURE__ */ __name((fastify) => {
1197
+ fastify.get("/relative-redirect/:value", {
1187
1198
  schema: relativeRedirectSchema
1188
1199
  }, async (request, reply) => {
1189
1200
  const value = request.params.value;
@@ -1232,79 +1243,204 @@ var redirectToSchema = {
1232
1243
  additionalProperties: false
1233
1244
  }
1234
1245
  };
1235
- var redirectToRoute = /* @__PURE__ */ __name((fastify2) => {
1246
+ var redirectToRoute = /* @__PURE__ */ __name((fastify) => {
1236
1247
  const handler = /* @__PURE__ */ __name(async (request, reply) => {
1237
1248
  const { url, status_code } = request.query;
1238
1249
  const statusCode = status_code ? Number(status_code) : 302;
1239
1250
  await reply.redirect(url, statusCode);
1240
1251
  }, "handler");
1241
- fastify2.get("/redirect-to", {
1252
+ fastify.get("/redirect-to", {
1242
1253
  schema: redirectToSchema
1243
1254
  }, handler);
1244
- fastify2.post("/redirect-to", {
1255
+ fastify.post("/redirect-to", {
1245
1256
  schema: redirectToSchema
1246
1257
  }, handler);
1247
- fastify2.put("/redirect-to", {
1258
+ fastify.put("/redirect-to", {
1248
1259
  schema: redirectToSchema
1249
1260
  }, handler);
1250
- fastify2.patch("/redirect-to", {
1261
+ fastify.patch("/redirect-to", {
1251
1262
  schema: redirectToSchema
1252
1263
  }, handler);
1253
- fastify2.delete("/redirect-to", {
1264
+ fastify.delete("/redirect-to", {
1254
1265
  schema: redirectToSchema
1255
1266
  }, handler);
1256
1267
  }, "redirectToRoute");
1257
1268
 
1258
- // src/index.ts
1259
- var fastify = Fastify(fastifyConfig);
1260
- await fastify.register(fastifyStatic, {
1261
- root: path.resolve("./node_modules/@scalar/api-reference/dist"),
1262
- prefix: "/scalar"
1263
- });
1264
- await fastify.register(fastifyStatic, {
1265
- root: path.resolve("./public"),
1266
- decorateReply: false
1267
- });
1268
- await fastify.register(fastifyHelmet);
1269
- await fastify.register(fastifySwagger, fastifySwaggerConfig);
1270
- await registerSwaggerUi(fastify);
1271
- await fastify.register(indexRoute);
1272
- await fastify.register(sitemapRoute);
1273
- await fastify.register(getRoute);
1274
- await fastify.register(postRoute);
1275
- await fastify.register(deleteRoute);
1276
- await fastify.register(putRoute);
1277
- await fastify.register(patchRoute);
1278
- await fastify.register(statusCodeRoute);
1279
- await fastify.register(ipRoute);
1280
- await fastify.register(headersRoute);
1281
- await fastify.register(userAgentRoute);
1282
- await fastify.register(cacheRoutes);
1283
- await fastify.register(etagRoutes);
1284
- await fastify.register(responseHeadersRoutes);
1285
- await fastify.register(absoluteRedirectRoute);
1286
- await fastify.register(relativeRedirectRoute);
1287
- await fastify.register(redirectToRoute);
1288
- var start = /* @__PURE__ */ __name(async () => {
1289
- try {
1290
- let port = 3e3;
1291
- if (process.env.PORT) {
1292
- port = Number.parseInt(process.env.PORT, 10);
1269
+ // src/mock-http.ts
1270
+ var MockHttp = class extends Hookified {
1271
+ static {
1272
+ __name(this, "MockHttp");
1273
+ }
1274
+ _port = 3e3;
1275
+ _host = "0.0.0.0";
1276
+ _helmet = true;
1277
+ _apiDocs = true;
1278
+ _httpBin = {
1279
+ httpMethods: true,
1280
+ redirects: true,
1281
+ requestInspection: true,
1282
+ responseInspection: true,
1283
+ statusCodes: true
1284
+ };
1285
+ // eslint-disable-next-line new-cap
1286
+ _server = Fastify();
1287
+ constructor(options) {
1288
+ super(options?.hookOptions);
1289
+ if (options?.port) {
1290
+ this._port = options.port;
1293
1291
  }
1294
- let host = "0.0.0.0";
1295
- if (process.env.HOST) {
1296
- host = process.env.HOST;
1292
+ if (options?.host) {
1293
+ this._host = options.host;
1297
1294
  }
1298
- await fastify.listen({
1299
- port,
1300
- host
1301
- });
1302
- } catch (error) {
1303
- fastify.log.error(error);
1304
- process.exit(1);
1295
+ if (options?.helmet !== void 0) {
1296
+ this._helmet = options.helmet;
1297
+ }
1298
+ if (options?.apiDocs !== void 0) {
1299
+ this._apiDocs = options.apiDocs;
1300
+ }
1301
+ if (options?.httpBin !== void 0) {
1302
+ this._httpBin = options.httpBin;
1303
+ }
1304
+ }
1305
+ get port() {
1306
+ return this._port;
1307
+ }
1308
+ set port(port) {
1309
+ this._port = port;
1310
+ }
1311
+ get host() {
1312
+ return this._host;
1313
+ }
1314
+ set host(host) {
1315
+ this._host = host;
1316
+ }
1317
+ get helmet() {
1318
+ return this._helmet;
1319
+ }
1320
+ set helmet(helmet) {
1321
+ this._helmet = helmet;
1322
+ }
1323
+ get apiDocs() {
1324
+ return this._apiDocs;
1325
+ }
1326
+ // eslint-disable-next-line unicorn/prevent-abbreviations
1327
+ set apiDocs(apiDocs) {
1328
+ this._apiDocs = apiDocs;
1329
+ }
1330
+ get httpBin() {
1331
+ return this._httpBin;
1332
+ }
1333
+ set httpBin(httpBinary) {
1334
+ this._httpBin = httpBinary;
1335
+ }
1336
+ get server() {
1337
+ return this._server;
1338
+ }
1339
+ set server(server) {
1340
+ this._server = server;
1341
+ }
1342
+ async start() {
1343
+ try {
1344
+ const { port, host } = this;
1345
+ if (this._server) {
1346
+ await this._server.close();
1347
+ }
1348
+ this._server = Fastify(fastifyConfig);
1349
+ await this._server.register(fastifyStatic, {
1350
+ root: path.resolve("./node_modules/@scalar/api-reference/dist"),
1351
+ prefix: "/scalar"
1352
+ });
1353
+ await this.server.register(fastifyStatic, {
1354
+ root: path.resolve("./public"),
1355
+ decorateReply: false
1356
+ });
1357
+ await this._server.register(sitemapRoute);
1358
+ if (this._helmet) {
1359
+ await this._server.register(fastifyHelmet);
1360
+ }
1361
+ if (this._apiDocs) {
1362
+ await this.registerApiDocs();
1363
+ }
1364
+ const { httpMethods, redirects, requestInspection, responseInspection, statusCodes: statusCodes2 } = this._httpBin;
1365
+ if (httpMethods) {
1366
+ await this.registerHttpMethods();
1367
+ }
1368
+ if (statusCodes2) {
1369
+ await this.registerStatusCodeRoutes();
1370
+ }
1371
+ if (requestInspection) {
1372
+ await this.registerRequestInspectionRoutes();
1373
+ }
1374
+ if (responseInspection) {
1375
+ await this.registerResponseInspectionRoutes();
1376
+ }
1377
+ if (redirects) {
1378
+ await this.registerRedirectRoutes();
1379
+ }
1380
+ await this._server.listen({
1381
+ port,
1382
+ host
1383
+ });
1384
+ } catch (error) {
1385
+ this._server.log.error(error);
1386
+ }
1387
+ }
1388
+ async close() {
1389
+ await this._server.close();
1390
+ }
1391
+ async registerApiDocs(fastifyInstance) {
1392
+ const fastify = fastifyInstance ?? this._server;
1393
+ await fastify.register(fastifySwagger, fastifySwaggerConfig);
1394
+ await registerSwaggerUi(fastify);
1395
+ await fastify.register(indexRoute);
1396
+ }
1397
+ async registerHttpMethods(fastifyInstance) {
1398
+ const fastify = fastifyInstance ?? this._server;
1399
+ await fastify.register(getRoute);
1400
+ await fastify.register(postRoute);
1401
+ await fastify.register(deleteRoute);
1402
+ await fastify.register(putRoute);
1403
+ await fastify.register(patchRoute);
1404
+ }
1405
+ async registerStatusCodeRoutes(fastifyInstance) {
1406
+ const fastify = fastifyInstance ?? this._server;
1407
+ await fastify.register(statusCodeRoute);
1408
+ }
1409
+ async registerRequestInspectionRoutes(fastifyInstance) {
1410
+ const fastify = fastifyInstance ?? this._server;
1411
+ await fastify.register(ipRoute);
1412
+ await fastify.register(headersRoute);
1413
+ await fastify.register(userAgentRoute);
1414
+ }
1415
+ async registerResponseInspectionRoutes(fastifyInstance) {
1416
+ const fastify = fastifyInstance ?? this._server;
1417
+ await fastify.register(cacheRoutes);
1418
+ await fastify.register(etagRoutes);
1419
+ await fastify.register(responseHeadersRoutes);
1420
+ }
1421
+ async registerRedirectRoutes(fastifyInstance) {
1422
+ const fastify = fastifyInstance ?? this._server;
1423
+ await fastify.register(absoluteRedirectRoute);
1424
+ await fastify.register(relativeRedirectRoute);
1425
+ await fastify.register(redirectToRoute);
1426
+ }
1427
+ };
1428
+
1429
+ // src/index.ts
1430
+ var start = /* @__PURE__ */ __name(async () => {
1431
+ const mockHttp = new MockHttp();
1432
+ if (process.env.PORT) {
1433
+ mockHttp.port = Number.parseInt(process.env.PORT, 10);
1434
+ }
1435
+ if (process.env.HOST) {
1436
+ mockHttp.host = process.env.HOST;
1305
1437
  }
1438
+ await mockHttp.start();
1439
+ return mockHttp;
1306
1440
  }, "start");
1307
1441
  await start();
1308
1442
  export {
1443
+ MockHttp as default,
1444
+ MockHttp as mockhttp,
1309
1445
  start
1310
1446
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jaredwray/mockhttp",
3
- "version": "0.3.0",
3
+ "version": "0.4.1",
4
4
  "description": "Mock Http - Easy way to mock http with httpbin replacement",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -36,23 +36,24 @@
36
36
  },
37
37
  "homepage": "https://github.com/jaredwray/mockhttp#readme",
38
38
  "devDependencies": {
39
- "@swc/core": "^1.10.9",
40
- "@types/html-escaper": "^3.0.2",
41
- "@vitest/coverage-v8": "^3.0.4",
39
+ "@swc/core": "^1.11.5",
40
+ "@types/html-escaper": "^3.0.4",
41
+ "@vitest/coverage-v8": "^3.0.7",
42
42
  "rimraf": "^6.0.1",
43
- "tsup": "^8.3.6",
44
- "tsx": "^4.19.2",
45
- "typescript": "^5.7.3",
46
- "vitest": "^3.0.4",
43
+ "tsup": "^8.4.0",
44
+ "tsx": "^4.19.3",
45
+ "typescript": "^5.8.2",
46
+ "vitest": "^3.0.7",
47
47
  "xo": "^0.60.0"
48
48
  },
49
49
  "dependencies": {
50
50
  "@fastify/helmet": "^13.0.1",
51
- "@fastify/static": "^8.0.4",
51
+ "@fastify/static": "^8.1.1",
52
52
  "@fastify/swagger": "^9.4.2",
53
- "@fastify/swagger-ui": "^5.2.1",
54
- "@scalar/api-reference": "^1.25.108",
53
+ "@fastify/swagger-ui": "^5.2.2",
54
+ "@scalar/api-reference": "^1.25.124",
55
55
  "fastify": "^5.2.1",
56
+ "hookified": "^1.7.1",
56
57
  "html-escaper": "^3.0.3",
57
58
  "pino": "^9.6.0",
58
59
  "pino-pretty": "^13.0.0"
@@ -61,5 +62,12 @@
61
62
  "dist",
62
63
  "public",
63
64
  "LICENCE"
64
- ]
65
+ ],
66
+ "pnpm": {
67
+ "onlyBuiltDependencies": [
68
+ "@swc/core",
69
+ "esbuild",
70
+ "vue-demi"
71
+ ]
72
+ }
65
73
  }