@jaredwray/mockhttp 0.3.0 → 0.4.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/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,7 +33,7 @@ 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
 
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.0",
136
22
  description: "Mock Http - Easy way to mock http with httpbin replacement",
137
23
  type: "module",
138
24
  main: "dist/index.js",
@@ -185,6 +71,7 @@ var package_default = {
185
71
  "@fastify/swagger-ui": "^5.2.1",
186
72
  "@scalar/api-reference": "^1.25.108",
187
73
  fastify: "^5.2.1",
74
+ hookified: "^1.7.0",
188
75
  "html-escaper": "^3.0.3",
189
76
  pino: "^9.6.0",
190
77
  "pino-pretty": "^13.0.0"
@@ -223,8 +110,8 @@ var fastifySwaggerConfig = {
223
110
  ]
224
111
  }
225
112
  };
226
- var registerSwaggerUi = /* @__PURE__ */ __name(async (fastify2) => {
227
- await fastify2.register(fastifySwaggerUi, {
113
+ var registerSwaggerUi = /* @__PURE__ */ __name(async (fastify) => {
114
+ await fastify.register(fastifySwaggerUi, {
228
115
  routePrefix: "/docs",
229
116
  uiConfig: {
230
117
  docExpansion: "none",
@@ -246,110 +133,122 @@ var registerSwaggerUi = /* @__PURE__ */ __name(async (fastify2) => {
246
133
  });
247
134
  }, "registerSwaggerUi");
248
135
 
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
- ]
136
+ // src/fastify-config.ts
137
+ var fastifyConfig = {
138
+ logger: {
139
+ transport: {
140
+ target: "pino-pretty",
141
+ options: {
142
+ colorize: true,
143
+ translateTime: true,
144
+ ignore: "pid,hostname",
145
+ singleLine: true
146
+ }
268
147
  }
269
148
  }
270
149
  };
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
150
 
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
- ]
151
+ // src/routes/index.ts
152
+ var indexRoute = /* @__PURE__ */ __name((fastify) => {
153
+ fastify.get("/", {
154
+ schema: {
155
+ hide: true
307
156
  }
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
- };
157
+ }, async (_request, reply) => {
158
+ const redocHtml = `
159
+ <!doctype html>
160
+ <html>
161
+ <head>
162
+ <title>{ mockhttp }</title>
163
+ <meta charset="utf-8" />
164
+ <meta
165
+ name="viewport"
166
+ content="width=device-width, initial-scale=1" />
167
+ <style>
168
+ :root {
169
+ --scalar-custom-header-height: 70px;
170
+ }
171
+ .custom-header {
172
+ height: var(--scalar-custom-header-height);
173
+ background-color: var(--scalar-background-1);
174
+ box-shadow: inset 0 -1px 0 var(--scalar-border-color);
175
+ color: var(--scalar-color-1);
176
+ font-size: var(--scalar-font-size-2);
177
+ padding: 0 18px;
178
+ position: sticky;
179
+ justify-content: space-between;
180
+ top: 0;
181
+ z-index: 100;
182
+ }
183
+ .custom-header,
184
+ .custom-header nav {
185
+ display: flex;
186
+ align-items: center;
187
+ gap: 18px;
188
+ }
189
+
190
+ .custom-header img {
191
+ height: var(--scalar-custom-header-height);
192
+ object-fit: contain;
193
+ }
194
+
195
+ .custom-header a:hover {
196
+ color: var(--scalar-color-2);
197
+ }
198
+
199
+ .github-corner {
200
+ fill: var(--scalar-border-color) !important;
201
+ }
202
+
203
+ </style>
204
+ </head>
205
+ <body>
206
+ <header class="custom-header scalar-app">
207
+ <a href="/"><img src="/logo.svg" alt="{ mockhttp }" /></a>
208
+ <a href="https://github.com/jaredwray/mockhttp" class="github-corner" aria-label="View source on GitHub">
209
+ <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>
210
+ </header>
211
+
212
+ <script
213
+ id="api-reference"
214
+ data-url="/docs/json"></script>
215
+
216
+ <script src="/scalar/browser/standalone.js"></script>
217
+ </body>
218
+ </html>
219
+ `;
220
+ await reply.type("text/html").send(redocHtml);
318
221
  });
319
- }, "headersRoute");
222
+ }, "indexRoute");
320
223
 
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
- ]
224
+ // src/routes/sitemap.ts
225
+ var sitemapRoute = /* @__PURE__ */ __name((fastify) => {
226
+ fastify.get("/sitemap.xml", {
227
+ schema: {
228
+ hide: true
229
+ }
230
+ }, async (_request, reply) => {
231
+ try {
232
+ const host = _request.headers.host ?? "mockhttp.org";
233
+ const isSecure = _request.headers["x-forwarded-proto"]?.includes("https");
234
+ const protocol = isSecure ? "https" : "http";
235
+ const xml = `<?xml version="1.0" encoding="UTF-8"?>
236
+ <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
237
+ <url>
238
+ <loc>${protocol}://${host}/</loc>
239
+ <changefreq>monthly</changefreq>
240
+ <priority>1.0</priority>
241
+ </url>
242
+ </urlset>`;
243
+ await reply.type("text/xml").send(xml);
244
+ } catch (error) {
245
+ fastify.log.error(error);
246
+ await reply.status(500).send({
247
+ error: "Internal Server Error"
248
+ });
340
249
  }
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
250
  });
352
- }, "userAgentRoute");
251
+ }, "sitemapRoute");
353
252
 
354
253
  // src/routes/http-methods/get.ts
355
254
  var getSchema = {
@@ -398,8 +297,8 @@ var getSchema = {
398
297
  }
399
298
  }
400
299
  };
401
- var getRoute = /* @__PURE__ */ __name((fastify2) => {
402
- fastify2.get("/get", {
300
+ var getRoute = /* @__PURE__ */ __name((fastify) => {
301
+ fastify.get("/get", {
403
302
  schema: getSchema
404
303
  }, async (request, _reply) => ({
405
304
  method: "GET",
@@ -446,8 +345,8 @@ var postSchema = {
446
345
  }
447
346
  }
448
347
  };
449
- var postRoute = /* @__PURE__ */ __name((fastify2) => {
450
- fastify2.post("/post", {
348
+ var postRoute = /* @__PURE__ */ __name((fastify) => {
349
+ fastify.post("/post", {
451
350
  schema: postSchema
452
351
  }, async (request) => ({
453
352
  method: "POST",
@@ -494,8 +393,8 @@ var deleteSchema = {
494
393
  }
495
394
  }
496
395
  };
497
- var deleteRoute = /* @__PURE__ */ __name((fastify2) => {
498
- fastify2.delete("/delete", {
396
+ var deleteRoute = /* @__PURE__ */ __name((fastify) => {
397
+ fastify.delete("/delete", {
499
398
  schema: deleteSchema
500
399
  }, async (request) => ({
501
400
  method: "DELETE",
@@ -542,8 +441,8 @@ var putSchema = {
542
441
  }
543
442
  }
544
443
  };
545
- var putRoute = /* @__PURE__ */ __name((fastify2) => {
546
- fastify2.put("/put", {
444
+ var putRoute = /* @__PURE__ */ __name((fastify) => {
445
+ fastify.put("/put", {
547
446
  schema: putSchema
548
447
  }, async (request) => ({
549
448
  method: "PUT",
@@ -590,8 +489,8 @@ var patchSchema = {
590
489
  }
591
490
  }
592
491
  };
593
- var patchRoute = /* @__PURE__ */ __name((fastify2) => {
594
- fastify2.patch("/patch", {
492
+ var patchRoute = /* @__PURE__ */ __name((fastify) => {
493
+ fastify.patch("/patch", {
595
494
  schema: patchSchema
596
495
  }, async (request) => ({
597
496
  method: "PATCH",
@@ -882,7 +781,7 @@ var statusSchema = {
882
781
  }
883
782
  }
884
783
  };
885
- var statusCodeRoute = /* @__PURE__ */ __name((fastify2) => {
784
+ var statusCodeRoute = /* @__PURE__ */ __name((fastify) => {
886
785
  const handler = /* @__PURE__ */ __name(async (request, reply) => {
887
786
  let { code } = request.params;
888
787
  if (!code || !statusCodes.map((status) => status.code).includes(Number(code))) {
@@ -892,23 +791,128 @@ var statusCodeRoute = /* @__PURE__ */ __name((fastify2) => {
892
791
  status: `Response with status code ${code}`
893
792
  });
894
793
  }, "handler");
895
- fastify2.get("/status/:code?", {
794
+ fastify.get("/status/:code?", {
896
795
  schema: statusSchema
897
796
  }, handler);
898
- fastify2.post("/status/:code?", {
797
+ fastify.post("/status/:code?", {
899
798
  schema: statusSchema
900
799
  }, handler);
901
- fastify2.put("/status/:code?", {
800
+ fastify.put("/status/:code?", {
902
801
  schema: statusSchema
903
802
  }, handler);
904
- fastify2.patch("/status/:code?", {
803
+ fastify.patch("/status/:code?", {
905
804
  schema: statusSchema
906
805
  }, handler);
907
- fastify2.delete("/status/:code?", {
806
+ fastify.delete("/status/:code?", {
908
807
  schema: statusSchema
909
808
  }, handler);
910
809
  }, "statusCodeRoute");
911
810
 
811
+ // src/routes/request-inspection/ip.ts
812
+ var ipSchema = {
813
+ description: "Returns the IP address of the client",
814
+ tags: [
815
+ "Request Inspection"
816
+ ],
817
+ response: {
818
+ // eslint-disable-next-line @typescript-eslint/naming-convention
819
+ 200: {
820
+ type: "object",
821
+ properties: {
822
+ ip: {
823
+ type: "string",
824
+ description: "The IP address of the client"
825
+ }
826
+ },
827
+ required: [
828
+ "ip"
829
+ ]
830
+ }
831
+ }
832
+ };
833
+ var ipRoute = /* @__PURE__ */ __name((fastify) => {
834
+ fastify.get("/ip", {
835
+ schema: ipSchema
836
+ }, async (request) => {
837
+ const cloudflareIp = request.headers["cf-connecting-ip"];
838
+ const forwardedIp = request.headers["x-forwarded-for"];
839
+ const remoteIp = request.ip;
840
+ const ip = cloudflareIp ?? (typeof forwardedIp === "string" ? forwardedIp.split(",")[0].trim() : remoteIp);
841
+ return {
842
+ ip
843
+ };
844
+ });
845
+ }, "ipRoute");
846
+
847
+ // src/routes/request-inspection/headers.ts
848
+ var headersSchema = {
849
+ description: "Returns all headers of the client request",
850
+ tags: [
851
+ "Request Inspection"
852
+ ],
853
+ response: {
854
+ // eslint-disable-next-line @typescript-eslint/naming-convention
855
+ 200: {
856
+ type: "object",
857
+ properties: {
858
+ headers: {
859
+ type: "object",
860
+ additionalProperties: {
861
+ type: "string"
862
+ },
863
+ description: "All headers of the client request"
864
+ }
865
+ },
866
+ required: [
867
+ "headers"
868
+ ]
869
+ }
870
+ }
871
+ };
872
+ var headersRoute = /* @__PURE__ */ __name((fastify) => {
873
+ fastify.get("/headers", {
874
+ schema: headersSchema
875
+ }, async (request) => {
876
+ const headers = request.headers;
877
+ return {
878
+ headers
879
+ };
880
+ });
881
+ }, "headersRoute");
882
+
883
+ // src/routes/request-inspection/user-agent.ts
884
+ var userAgentSchema = {
885
+ description: "Returns the User-Agent header of the client request",
886
+ tags: [
887
+ "Request Inspection"
888
+ ],
889
+ response: {
890
+ // eslint-disable-next-line @typescript-eslint/naming-convention
891
+ 200: {
892
+ type: "object",
893
+ properties: {
894
+ userAgent: {
895
+ type: "string",
896
+ description: "The User-Agent header of the client request"
897
+ }
898
+ },
899
+ required: [
900
+ "userAgent"
901
+ ]
902
+ }
903
+ }
904
+ };
905
+ var userAgentRoute = /* @__PURE__ */ __name((fastify) => {
906
+ fastify.get("/user-agent", {
907
+ schema: userAgentSchema
908
+ }, async (request) => {
909
+ const userAgent = request.headers["user-agent"];
910
+ return {
911
+ userAgent
912
+ };
913
+ });
914
+ }, "userAgentRoute");
915
+
912
916
  // src/routes/response-inspection/cache.ts
913
917
  var cacheGetSchema = {
914
918
  description: "Handles cache validation and retrieval.",
@@ -953,8 +957,8 @@ var cacheSetSchema = {
953
957
  }
954
958
  }
955
959
  };
956
- var cacheRoutes = /* @__PURE__ */ __name((fastify2) => {
957
- fastify2.get("/cache", {
960
+ var cacheRoutes = /* @__PURE__ */ __name((fastify) => {
961
+ fastify.get("/cache", {
958
962
  schema: cacheGetSchema
959
963
  }, async (request, reply) => {
960
964
  const ifModifiedSince = request.headers["if-modified-since"];
@@ -965,7 +969,7 @@ var cacheRoutes = /* @__PURE__ */ __name((fastify2) => {
965
969
  }
966
970
  await reply.send("Cache content or resource response here.");
967
971
  });
968
- fastify2.get("/cache/:value", {
972
+ fastify.get("/cache/:value", {
969
973
  schema: cacheSetSchema
970
974
  }, async (request, reply) => {
971
975
  const { value } = request.params;
@@ -1009,8 +1013,8 @@ var etagSchema = {
1009
1013
  }
1010
1014
  }
1011
1015
  };
1012
- var etagRoutes = /* @__PURE__ */ __name((fastify2) => {
1013
- fastify2.get("/etag/:etag", {
1016
+ var etagRoutes = /* @__PURE__ */ __name((fastify) => {
1017
+ fastify.get("/etag/:etag", {
1014
1018
  schema: etagSchema
1015
1019
  }, async (request, reply) => {
1016
1020
  const { etag } = request.params;
@@ -1049,8 +1053,8 @@ var responseHeadersSchema = {
1049
1053
  }
1050
1054
  }
1051
1055
  };
1052
- var responseHeadersRoutes = /* @__PURE__ */ __name((fastify2) => {
1053
- fastify2.get("/response-headers", {
1056
+ var responseHeadersRoutes = /* @__PURE__ */ __name((fastify) => {
1057
+ fastify.get("/response-headers", {
1054
1058
  schema: responseHeadersSchema
1055
1059
  }, async (request, reply) => {
1056
1060
  const queryParameters = request.query;
@@ -1063,7 +1067,7 @@ var responseHeadersRoutes = /* @__PURE__ */ __name((fastify2) => {
1063
1067
  ]));
1064
1068
  await reply.send(cleanedResponse);
1065
1069
  });
1066
- fastify2.post("/response-headers", {
1070
+ fastify.post("/response-headers", {
1067
1071
  schema: responseHeadersSchema
1068
1072
  }, async (request, reply) => {
1069
1073
  const queryParameters = request.query ?? {};
@@ -1121,8 +1125,8 @@ var absoluteRedirectSchema = {
1121
1125
  }
1122
1126
  }
1123
1127
  };
1124
- var absoluteRedirectRoute = /* @__PURE__ */ __name((fastify2) => {
1125
- fastify2.get("/absolute-redirect/:value", {
1128
+ var absoluteRedirectRoute = /* @__PURE__ */ __name((fastify) => {
1129
+ fastify.get("/absolute-redirect/:value", {
1126
1130
  schema: absoluteRedirectSchema
1127
1131
  }, async (request, reply) => {
1128
1132
  const value = request.params.value;
@@ -1182,8 +1186,8 @@ var relativeRedirectSchema = {
1182
1186
  }
1183
1187
  }
1184
1188
  };
1185
- var relativeRedirectRoute = /* @__PURE__ */ __name((fastify2) => {
1186
- fastify2.get("/relative-redirect/:value", {
1189
+ var relativeRedirectRoute = /* @__PURE__ */ __name((fastify) => {
1190
+ fastify.get("/relative-redirect/:value", {
1187
1191
  schema: relativeRedirectSchema
1188
1192
  }, async (request, reply) => {
1189
1193
  const value = request.params.value;
@@ -1232,79 +1236,204 @@ var redirectToSchema = {
1232
1236
  additionalProperties: false
1233
1237
  }
1234
1238
  };
1235
- var redirectToRoute = /* @__PURE__ */ __name((fastify2) => {
1239
+ var redirectToRoute = /* @__PURE__ */ __name((fastify) => {
1236
1240
  const handler = /* @__PURE__ */ __name(async (request, reply) => {
1237
1241
  const { url, status_code } = request.query;
1238
1242
  const statusCode = status_code ? Number(status_code) : 302;
1239
1243
  await reply.redirect(url, statusCode);
1240
1244
  }, "handler");
1241
- fastify2.get("/redirect-to", {
1245
+ fastify.get("/redirect-to", {
1242
1246
  schema: redirectToSchema
1243
1247
  }, handler);
1244
- fastify2.post("/redirect-to", {
1248
+ fastify.post("/redirect-to", {
1245
1249
  schema: redirectToSchema
1246
1250
  }, handler);
1247
- fastify2.put("/redirect-to", {
1251
+ fastify.put("/redirect-to", {
1248
1252
  schema: redirectToSchema
1249
1253
  }, handler);
1250
- fastify2.patch("/redirect-to", {
1254
+ fastify.patch("/redirect-to", {
1251
1255
  schema: redirectToSchema
1252
1256
  }, handler);
1253
- fastify2.delete("/redirect-to", {
1257
+ fastify.delete("/redirect-to", {
1254
1258
  schema: redirectToSchema
1255
1259
  }, handler);
1256
1260
  }, "redirectToRoute");
1257
1261
 
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);
1262
+ // src/mock-http.ts
1263
+ var MockHttp = class extends Hookified {
1264
+ static {
1265
+ __name(this, "MockHttp");
1266
+ }
1267
+ _port = 3e3;
1268
+ _host = "0.0.0.0";
1269
+ _helmet = true;
1270
+ _apiDocs = true;
1271
+ _httpBin = {
1272
+ httpMethods: true,
1273
+ redirects: true,
1274
+ requestInspection: true,
1275
+ responseInspection: true,
1276
+ statusCodes: true
1277
+ };
1278
+ // eslint-disable-next-line new-cap
1279
+ _server = Fastify();
1280
+ constructor(options) {
1281
+ super(options?.hookOptions);
1282
+ if (options?.port) {
1283
+ this._port = options.port;
1293
1284
  }
1294
- let host = "0.0.0.0";
1295
- if (process.env.HOST) {
1296
- host = process.env.HOST;
1285
+ if (options?.host) {
1286
+ this._host = options.host;
1297
1287
  }
1298
- await fastify.listen({
1299
- port,
1300
- host
1301
- });
1302
- } catch (error) {
1303
- fastify.log.error(error);
1304
- process.exit(1);
1288
+ if (options?.helmet !== void 0) {
1289
+ this._helmet = options.helmet;
1290
+ }
1291
+ if (options?.apiDocs !== void 0) {
1292
+ this._apiDocs = options.apiDocs;
1293
+ }
1294
+ if (options?.httpBin !== void 0) {
1295
+ this._httpBin = options.httpBin;
1296
+ }
1297
+ }
1298
+ get port() {
1299
+ return this._port;
1300
+ }
1301
+ set port(port) {
1302
+ this._port = port;
1303
+ }
1304
+ get host() {
1305
+ return this._host;
1306
+ }
1307
+ set host(host) {
1308
+ this._host = host;
1309
+ }
1310
+ get helmet() {
1311
+ return this._helmet;
1312
+ }
1313
+ set helmet(helmet) {
1314
+ this._helmet = helmet;
1315
+ }
1316
+ get apiDocs() {
1317
+ return this._apiDocs;
1318
+ }
1319
+ // eslint-disable-next-line unicorn/prevent-abbreviations
1320
+ set apiDocs(apiDocs) {
1321
+ this._apiDocs = apiDocs;
1322
+ }
1323
+ get httpBin() {
1324
+ return this._httpBin;
1325
+ }
1326
+ set httpBin(httpBinary) {
1327
+ this._httpBin = httpBinary;
1328
+ }
1329
+ get server() {
1330
+ return this._server;
1331
+ }
1332
+ set server(server) {
1333
+ this._server = server;
1334
+ }
1335
+ async start() {
1336
+ try {
1337
+ const { port, host } = this;
1338
+ if (this._server) {
1339
+ await this._server.close();
1340
+ }
1341
+ this._server = Fastify(fastifyConfig);
1342
+ await this._server.register(fastifyStatic, {
1343
+ root: path.resolve("./node_modules/@scalar/api-reference/dist"),
1344
+ prefix: "/scalar"
1345
+ });
1346
+ await this.server.register(fastifyStatic, {
1347
+ root: path.resolve("./public"),
1348
+ decorateReply: false
1349
+ });
1350
+ await this._server.register(sitemapRoute);
1351
+ if (this._helmet) {
1352
+ await this._server.register(fastifyHelmet);
1353
+ }
1354
+ if (this._apiDocs) {
1355
+ await this.registerApiDocs();
1356
+ }
1357
+ const { httpMethods, redirects, requestInspection, responseInspection, statusCodes: statusCodes2 } = this._httpBin;
1358
+ if (httpMethods) {
1359
+ await this.registerHttpMethods();
1360
+ }
1361
+ if (statusCodes2) {
1362
+ await this.registerStatusCodeRoutes();
1363
+ }
1364
+ if (requestInspection) {
1365
+ await this.registerRequestInspectionRoutes();
1366
+ }
1367
+ if (responseInspection) {
1368
+ await this.registerResponseInspectionRoutes();
1369
+ }
1370
+ if (redirects) {
1371
+ await this.registerRedirectRoutes();
1372
+ }
1373
+ await this._server.listen({
1374
+ port,
1375
+ host
1376
+ });
1377
+ } catch (error) {
1378
+ this._server.log.error(error);
1379
+ }
1380
+ }
1381
+ async close() {
1382
+ await this._server.close();
1383
+ }
1384
+ async registerApiDocs(fastifyInstance) {
1385
+ const fastify = fastifyInstance ?? this._server;
1386
+ await fastify.register(fastifySwagger, fastifySwaggerConfig);
1387
+ await registerSwaggerUi(fastify);
1388
+ await fastify.register(indexRoute);
1389
+ }
1390
+ async registerHttpMethods(fastifyInstance) {
1391
+ const fastify = fastifyInstance ?? this._server;
1392
+ await fastify.register(getRoute);
1393
+ await fastify.register(postRoute);
1394
+ await fastify.register(deleteRoute);
1395
+ await fastify.register(putRoute);
1396
+ await fastify.register(patchRoute);
1397
+ }
1398
+ async registerStatusCodeRoutes(fastifyInstance) {
1399
+ const fastify = fastifyInstance ?? this._server;
1400
+ await fastify.register(statusCodeRoute);
1401
+ }
1402
+ async registerRequestInspectionRoutes(fastifyInstance) {
1403
+ const fastify = fastifyInstance ?? this._server;
1404
+ await fastify.register(ipRoute);
1405
+ await fastify.register(headersRoute);
1406
+ await fastify.register(userAgentRoute);
1407
+ }
1408
+ async registerResponseInspectionRoutes(fastifyInstance) {
1409
+ const fastify = fastifyInstance ?? this._server;
1410
+ await fastify.register(cacheRoutes);
1411
+ await fastify.register(etagRoutes);
1412
+ await fastify.register(responseHeadersRoutes);
1413
+ }
1414
+ async registerRedirectRoutes(fastifyInstance) {
1415
+ const fastify = fastifyInstance ?? this._server;
1416
+ await fastify.register(absoluteRedirectRoute);
1417
+ await fastify.register(relativeRedirectRoute);
1418
+ await fastify.register(redirectToRoute);
1419
+ }
1420
+ };
1421
+
1422
+ // src/index.ts
1423
+ var start = /* @__PURE__ */ __name(async () => {
1424
+ const mockHttp = new MockHttp();
1425
+ if (process.env.PORT) {
1426
+ mockHttp.port = Number.parseInt(process.env.PORT, 10);
1427
+ }
1428
+ if (process.env.HOST) {
1429
+ mockHttp.host = process.env.HOST;
1305
1430
  }
1431
+ await mockHttp.start();
1432
+ return mockHttp;
1306
1433
  }, "start");
1307
1434
  await start();
1308
1435
  export {
1436
+ MockHttp as default,
1437
+ MockHttp as mockhttp,
1309
1438
  start
1310
1439
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jaredwray/mockhttp",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Mock Http - Easy way to mock http with httpbin replacement",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -53,6 +53,7 @@
53
53
  "@fastify/swagger-ui": "^5.2.1",
54
54
  "@scalar/api-reference": "^1.25.108",
55
55
  "fastify": "^5.2.1",
56
+ "hookified": "^1.7.0",
56
57
  "html-escaper": "^3.0.3",
57
58
  "pino": "^9.6.0",
58
59
  "pino-pretty": "^13.0.0"