@jaredwray/mockhttp 1.5.0 → 1.6.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
@@ -8,11 +8,11 @@
8
8
  [![Docker Pulls](https://img.shields.io/docker/pulls/jaredwray/mockhttp)](https://hub.docker.com/r/jaredwray/mockhttp)
9
9
  [![mockhttp.org](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fjaredwray.com%2Fapi%2Fmockhttp-traffic&query=%24.message&label=mockhttp.org)](https://mockhttp.org)
10
10
 
11
- A simple HTTP server that can be used to mock HTTP responses for testing purposes. Inspired by [httpbin](https://httpbin.org/) and built using `nodejs` and `fastify` with the idea of running it via https://mockhttp.org, via docker `jaredwray/mockhttp`, or nodejs `npm install jaredwray/mockhttp`.
11
+ A simple HTTP server that can be used to mock HTTP responses for testing purposes. Inspired by [httpbin](https://httpbin.org/) and built using `nodejs` and `fastify` with the idea of running it via https://mockhttp.org, via docker `jaredwray/mockhttp`, or nodejs `npm install @jaredwray/mockhttp`.
12
12
 
13
13
  # Features
14
14
  * All the features of [httpbin](https://httpbin.org/)
15
- * Taps - Inject custom responses for testing and develepment
15
+ * Taps - Inject custom responses for testing and development
16
16
  * `@fastify/helmet` built in by default
17
17
  * Built with `nodejs`, `typescript`, and `fastify`
18
18
  * Deploy via `docker` or `nodejs`
@@ -30,6 +30,7 @@ A simple HTTP server that can be used to mock HTTP responses for testing purpose
30
30
  - [Response Injection (Tap Feature)](#response-injection-tap-feature)
31
31
  - [Rate Limiting](#rate-limiting)
32
32
  - [Logging](#logging)
33
+ - [Flexible URL Matching](#flexible-url-matching)
33
34
  - [API Reference](#api-reference)
34
35
  - [About mockhttp.org](#about-mockhttporg)
35
36
  - [Contributing](#contributing)
@@ -608,6 +609,21 @@ You can also disable logging via the `LOGGING` environment variable:
608
609
  LOGGING=false node your-app.js
609
610
  ```
610
611
 
612
+ # Flexible URL Matching
613
+
614
+ MockHttp ignores trailing path segments that come after the parsable portion of a URL. This is useful when a client appends extra data to a known endpoint — instead of returning 404, MockHttp serves the closest matching route.
615
+
616
+ For example, all of these are served by `/status/:code`:
617
+
618
+ ```
619
+ GET /status/429
620
+ GET /status/429/
621
+ GET /status/429/foo
622
+ GET /status/429/foo/bar
623
+ ```
624
+
625
+ The rewrite preserves the query string and only triggers when a more specific route exists; URLs whose first path segment doesn't correspond to a registered route still return 404.
626
+
611
627
  # API Reference
612
628
 
613
629
  ## MockHttp Class
@@ -0,0 +1,494 @@
1
+ import { RateLimitPluginOptions } from "@fastify/rate-limit";
2
+ import { FastifyInstance, FastifyRequest } from "fastify";
3
+ import { Hookified, HookifiedOptions } from "hookified";
4
+
5
+ //#region src/certificate.d.ts
6
+ type CertificateOptions = {
7
+ /**
8
+ * Common Name for the certificate subject. Defaults to 'localhost'.
9
+ */
10
+ commonName?: string;
11
+ /**
12
+ * Subject Alternative Names. Defaults to [dns:localhost, ip:127.0.0.1, ip:::1].
13
+ */
14
+ altNames?: Array<{
15
+ type: "dns";
16
+ value: string;
17
+ } | {
18
+ type: "ip";
19
+ value: string;
20
+ }>;
21
+ /**
22
+ * Number of days the certificate is valid. Defaults to 365.
23
+ */
24
+ validityDays?: number;
25
+ /**
26
+ * RSA key size in bits. Defaults to 2048.
27
+ */
28
+ keySize?: number;
29
+ };
30
+ type CertificateResult = {
31
+ /**
32
+ * PEM-encoded X.509 certificate.
33
+ */
34
+ cert: string;
35
+ /**
36
+ * PEM-encoded PKCS#8 private key.
37
+ */
38
+ key: string;
39
+ };
40
+ type CertificateFileOptions = CertificateOptions & {
41
+ /**
42
+ * File path to write the certificate PEM.
43
+ */
44
+ certPath: string;
45
+ /**
46
+ * File path to write the private key PEM.
47
+ */
48
+ keyPath: string;
49
+ };
50
+ /**
51
+ * Generate a self-signed certificate using only Node.js built-in crypto.
52
+ * Returns PEM-encoded certificate and private key strings.
53
+ */
54
+ declare function generateCertificate(options?: CertificateOptions): CertificateResult;
55
+ /**
56
+ * Generate a self-signed certificate and write PEM files to disk.
57
+ * Returns the same CertificateResult as generateCertificate().
58
+ */
59
+ declare function generateCertificateFiles(options: CertificateFileOptions): Promise<CertificateResult>;
60
+ //#endregion
61
+ //#region src/tap-manager.d.ts
62
+ /**
63
+ * Configuration for the injected response
64
+ */
65
+ type InjectionResponse = {
66
+ /**
67
+ * The response body (can be string, object, or buffer)
68
+ */
69
+ response: string | Record<string, unknown> | Buffer;
70
+ /**
71
+ * HTTP status code for the response
72
+ * @default 200
73
+ */
74
+ statusCode?: number;
75
+ /**
76
+ * Response headers
77
+ */
78
+ headers?: Record<string, string | string[]>;
79
+ };
80
+ /**
81
+ * Criteria for matching requests to inject responses
82
+ */
83
+ type InjectionMatcher = {
84
+ /**
85
+ * URL path to match (supports wildcards with *)
86
+ * @example "/api/users" - exact match
87
+ * @example "/api/*" - wildcard match
88
+ */
89
+ url?: string;
90
+ /**
91
+ * Hostname to match
92
+ * @example "localhost"
93
+ * @example "api.example.com"
94
+ */
95
+ hostname?: string;
96
+ /**
97
+ * HTTP method to match
98
+ * @example "GET"
99
+ * @example "POST"
100
+ */
101
+ method?: string;
102
+ /**
103
+ * Request headers to match (all specified headers must match)
104
+ */
105
+ headers?: Record<string, string>;
106
+ };
107
+ /**
108
+ * Function that returns an injection response
109
+ */
110
+ type InjectionResponseFunction = (request: FastifyRequest) => InjectionResponse;
111
+ /**
112
+ * A tap represents an active injection that can be removed later
113
+ */
114
+ type InjectionTap = {
115
+ /**
116
+ * Unique identifier for this injection
117
+ */
118
+ id: string;
119
+ /**
120
+ * The response configuration or a function that returns it
121
+ */
122
+ response: InjectionResponse | InjectionResponseFunction;
123
+ /**
124
+ * The request matcher configuration
125
+ */
126
+ matcher?: InjectionMatcher;
127
+ };
128
+ /**
129
+ * Manages HTTP response injections for testing and mocking
130
+ */
131
+ declare class TapManager {
132
+ private _injections;
133
+ constructor();
134
+ /**
135
+ * Get all active injection taps
136
+ */
137
+ get injections(): Map<string, InjectionTap>;
138
+ /**
139
+ * Check if there are any active injections
140
+ */
141
+ get hasInjections(): boolean;
142
+ /**
143
+ * Inject a custom response for requests matching the given criteria
144
+ * @param response - The response configuration or a function that returns it
145
+ * @param matcher - Optional criteria to match requests
146
+ * @returns A tap object that can be used to remove the injection
147
+ */
148
+ inject(response: InjectionResponse | InjectionResponseFunction, matcher?: InjectionMatcher): InjectionTap;
149
+ /**
150
+ * Remove an injection by its tap object or ID
151
+ * @param tapOrId - The tap object or tap ID to remove
152
+ * @returns true if the injection was removed, false if it wasn't found
153
+ */
154
+ removeInjection(tapOrId: InjectionTap | string): boolean;
155
+ /**
156
+ * Find the first injection that matches the given request
157
+ * @param request - The Fastify request object
158
+ * @returns The matching injection tap, or undefined if no match
159
+ */
160
+ matchRequest(request: FastifyRequest): InjectionTap | undefined;
161
+ /**
162
+ * Clear all injections
163
+ */
164
+ clear(): void;
165
+ /**
166
+ * Check if a request matches the given matcher criteria
167
+ * @param request - The Fastify request object
168
+ * @param matcher - The matcher criteria (undefined matches all requests)
169
+ * @returns true if the request matches
170
+ */
171
+ private requestMatches;
172
+ /**
173
+ * Check if a URL matches a pattern (supports wildcards with *)
174
+ * @param url - The URL to test
175
+ * @param pattern - The pattern to match against
176
+ * @returns true if the URL matches the pattern
177
+ */
178
+ private urlMatches;
179
+ }
180
+ //#endregion
181
+ //#region src/mock-http.d.ts
182
+ type HttpBinOptions = {
183
+ httpMethods?: boolean;
184
+ redirects?: boolean;
185
+ requestInspection?: boolean;
186
+ responseInspection?: boolean;
187
+ statusCodes?: boolean;
188
+ responseFormats?: boolean;
189
+ cookies?: boolean;
190
+ anything?: boolean;
191
+ auth?: boolean;
192
+ images?: boolean;
193
+ dynamicData?: boolean;
194
+ };
195
+ type HttpsOptions = {
196
+ /**
197
+ * PEM-encoded certificate string, or file path to a certificate PEM file.
198
+ */
199
+ cert?: string;
200
+ /**
201
+ * PEM-encoded private key string, or file path to a private key PEM file.
202
+ */
203
+ key?: string;
204
+ /**
205
+ * If true, auto-generate a self-signed certificate. Defaults to true when no cert/key provided.
206
+ */
207
+ autoGenerate?: boolean;
208
+ /**
209
+ * Options for auto-generating a self-signed certificate. Only used when auto-generating.
210
+ */
211
+ certificateOptions?: CertificateOptions;
212
+ };
213
+ type MockHttpOptions = {
214
+ /**
215
+ * The port to listen on. If not provided, defaults to 3000 and detects the next available port if in use.
216
+ */
217
+ port?: number;
218
+ /**
219
+ * The host to listen on. If not provided, defaults to '0.0.0.0'.
220
+ */
221
+ host?: string;
222
+ /**
223
+ * Whether to automatically detect the next available port if the provided port is in use. Defaults
224
+ * to true.
225
+ */
226
+ autoDetectPort?: boolean;
227
+ /**
228
+ * Whether to use Helmet for security headers. Defaults to true.
229
+ */
230
+ helmet?: boolean;
231
+ /**
232
+ * Whether to use Swagger for API documentation. Defaults to true.
233
+ */
234
+ apiDocs?: boolean;
235
+ /**
236
+ * Whether to use Swagger UI. Defaults to true.
237
+ */
238
+ httpBin?: HttpBinOptions;
239
+ /**
240
+ * Rate limiting options. Defaults to 1000 requests per minute (localhost excluded).
241
+ * Set to undefined to disable rate limiting, or provide custom options to configure.
242
+ */
243
+ rateLimit?: boolean | RateLimitPluginOptions;
244
+ /**
245
+ * Hookified options.
246
+ */
247
+ hookOptions?: HookifiedOptions;
248
+ /**
249
+ * Whether to enable logging. Defaults to true.
250
+ */
251
+ logging?: boolean;
252
+ /**
253
+ * HTTPS configuration. Set to true to auto-generate a self-signed certificate,
254
+ * or provide an object with cert/key strings/paths or auto-generation options.
255
+ */
256
+ https?: boolean | HttpsOptions;
257
+ /**
258
+ * Whether to enable HTTP/2 support. When used with `https`, enables h2 (HTTP/2 over TLS).
259
+ * When used without `https`, enables h2c (HTTP/2 cleartext).
260
+ * Defaults to false.
261
+ */
262
+ http2?: boolean;
263
+ /**
264
+ * Whether to allow HTTP/1.1 fallback when using HTTP/2 with HTTPS (ALPN negotiation).
265
+ * Only applies when both `http2` and `https` are enabled.
266
+ * Defaults to true.
267
+ */
268
+ http1?: boolean;
269
+ };
270
+ declare class MockHttp extends Hookified {
271
+ private _port;
272
+ private _host;
273
+ private _autoDetectPort;
274
+ private _helmet;
275
+ private _apiDocs;
276
+ private _logging;
277
+ private _httpBin;
278
+ private _rateLimit?;
279
+ private _http2;
280
+ private _http1;
281
+ private _https;
282
+ private _httpsCredentials;
283
+ private _server;
284
+ private _taps;
285
+ constructor(options?: MockHttpOptions);
286
+ /**
287
+ * The port to listen on. If not provided, defaults to 3000 and detects the next available port if in use.
288
+ * @default 3000
289
+ */
290
+ get port(): number;
291
+ /**
292
+ * The port to listen on. If not provided, defaults to 3000 and detects the next available port if in use.
293
+ * @default 3000
294
+ */
295
+ set port(port: number);
296
+ /**
297
+ * The host to listen on. If not provided, defaults to 'localhost'.
298
+ * @default 'localhost'
299
+ */
300
+ get host(): string;
301
+ /**
302
+ * The host to listen on. If not provided, defaults to 'localhost'.
303
+ * @default 'localhost'
304
+ */
305
+ set host(host: string);
306
+ /**
307
+ * Whether to automatically detect the next available port if the provided port is in use. Defaults to true.
308
+ * @default true
309
+ */
310
+ get autoDetectPort(): boolean;
311
+ /**
312
+ * Whether to automatically detect the next available port if the provided port is in use. Defaults to true.
313
+ * @default true
314
+ */
315
+ set autoDetectPort(autoDetectPort: boolean);
316
+ /**
317
+ * Whether to use Helmet for security headers. Defaults to true.
318
+ * @default true
319
+ */
320
+ get helmet(): boolean;
321
+ /**
322
+ * Whether to use Helmet for security headers. Defaults to true.
323
+ * @default true
324
+ */
325
+ set helmet(helmet: boolean);
326
+ /**
327
+ * Whether to use Swagger for API documentation. Defaults to true.
328
+ * @default true
329
+ */
330
+ get apiDocs(): boolean;
331
+ /**
332
+ * Whether to use Swagger for API documentation. Defaults to true.
333
+ * @default true
334
+ */
335
+ set apiDocs(apiDocs: boolean);
336
+ /**
337
+ * Whether to enable logging. Defaults to true.
338
+ * @default true
339
+ */
340
+ get logging(): boolean;
341
+ /**
342
+ * Whether to enable logging. Defaults to true.
343
+ * @default true
344
+ */
345
+ set logging(logging: boolean);
346
+ /**
347
+ * HTTPS configuration. Set to true to auto-generate a self-signed certificate,
348
+ * or provide an HttpsOptions object with cert/key or auto-generation options.
349
+ */
350
+ get https(): HttpsOptions | undefined;
351
+ /**
352
+ * HTTPS configuration. Set to true to auto-generate a self-signed certificate,
353
+ * or provide an HttpsOptions object with cert/key or auto-generation options.
354
+ */
355
+ set https(value: boolean | HttpsOptions | undefined);
356
+ /**
357
+ * Whether the server is running in HTTPS mode.
358
+ */
359
+ get isHttps(): boolean;
360
+ /**
361
+ * Whether HTTP/2 support is enabled.
362
+ * @default false
363
+ */
364
+ get http2(): boolean;
365
+ /**
366
+ * Whether HTTP/2 support is enabled.
367
+ * @default false
368
+ */
369
+ set http2(http2: boolean);
370
+ /**
371
+ * Whether to allow HTTP/1.1 fallback when using HTTP/2 with HTTPS.
372
+ * @default true
373
+ */
374
+ get http1(): boolean;
375
+ /**
376
+ * Whether to allow HTTP/1.1 fallback when using HTTP/2 with HTTPS.
377
+ * @default true
378
+ */
379
+ set http1(http1: boolean);
380
+ /**
381
+ * HTTP Bin options. Defaults to all enabled.
382
+ */
383
+ get httpBin(): HttpBinOptions;
384
+ /**
385
+ * HTTP Bin options. Defaults to all enabled.
386
+ */
387
+ set httpBin(httpBinary: HttpBinOptions);
388
+ /**
389
+ * Rate limiting options. Defaults to 1000 requests per minute (localhost excluded).
390
+ * Set to undefined to disable rate limiting, or provide custom options to configure.
391
+ * @default { max: 1000, timeWindow: "1 minute", allowList: ["127.0.0.1", "::1"] }
392
+ */
393
+ get rateLimit(): RateLimitPluginOptions | undefined;
394
+ /**
395
+ * Rate limiting options. Defaults to 1000 requests per minute (localhost excluded).
396
+ * Set to undefined to disable rate limiting, or provide custom options to configure.
397
+ *
398
+ * Note: Changing this property requires restarting the server (close() then start()) for changes to take effect.
399
+ * @default { max: 1000, timeWindow: "1 minute", allowList: ["127.0.0.1", "::1"] }
400
+ */
401
+ set rateLimit(rateLimit: RateLimitPluginOptions | undefined);
402
+ /**
403
+ * The Fastify server instance.
404
+ */
405
+ get server(): FastifyInstance;
406
+ /**
407
+ * The Fastify server instance.
408
+ */
409
+ set server(server: FastifyInstance);
410
+ /**
411
+ * The TapManager instance for managing injection taps.
412
+ */
413
+ get taps(): TapManager;
414
+ /**
415
+ * The TapManager instance for managing injection taps.
416
+ */
417
+ set taps(taps: TapManager);
418
+ /**
419
+ * Start the Fastify server. If the server is already running, it will be closed and restarted.
420
+ */
421
+ start(): Promise<void>;
422
+ /**
423
+ * Close the Fastify server.
424
+ */
425
+ close(): Promise<void>;
426
+ /**
427
+ * Detect the next available port to run on.
428
+ * @returns The port that is available to run on
429
+ */
430
+ detectPort(): Promise<number>;
431
+ /**
432
+ * This will register the API documentation routes including openapi and swagger ui.
433
+ * @param fastifyInstance - the server instance to register the routes on.
434
+ */
435
+ registerApiDocs(fastifyInstance?: FastifyInstance): Promise<void>;
436
+ /**
437
+ * Register the HTTP methods routes.
438
+ * @param fastifyInstance - the server instance to register the routes on.
439
+ */
440
+ registerHttpMethods(fastifyInstance?: FastifyInstance): Promise<void>;
441
+ /**
442
+ * Register the status code routes.
443
+ * @param fastifyInstance - the server instance to register the routes on.
444
+ */
445
+ registerStatusCodeRoutes(fastifyInstance?: FastifyInstance): Promise<void>;
446
+ /**
447
+ * Register the request inspection routes.
448
+ * @param fastifyInstance - the server instance to register the routes on.
449
+ */
450
+ registerRequestInspectionRoutes(fastifyInstance?: FastifyInstance): Promise<void>;
451
+ /**
452
+ * Register the response inspection routes.
453
+ * @param fastifyInstance - the server instance to register the routes on.
454
+ */
455
+ registerResponseInspectionRoutes(fastifyInstance?: FastifyInstance): Promise<void>;
456
+ registerResponseFormatRoutes(fastifyInstance?: FastifyInstance): Promise<void>;
457
+ /**
458
+ * Register the redirect routes.
459
+ * @param fastifyInstance - the server instance to register the routes on.
460
+ */
461
+ registerRedirectRoutes(fastifyInstance?: FastifyInstance): Promise<void>;
462
+ /**
463
+ * Register the cookie routes.
464
+ * @param fastifyInstance - the server instance to register the routes on.
465
+ */
466
+ registerCookieRoutes(fastifyInstance?: FastifyInstance): Promise<void>;
467
+ /**
468
+ * Register the anything routes.
469
+ * @param fastifyInstance - the server instance to register the routes on.
470
+ */
471
+ registerAnythingRoutes(fastifyInstance?: FastifyInstance): Promise<void>;
472
+ /**
473
+ * Register the auth routes.
474
+ * @param fastifyInstance - the server instance to register the routes on.
475
+ */
476
+ registerAuthRoutes(fastifyInstance?: FastifyInstance): Promise<void>;
477
+ /**
478
+ * Register the image routes.
479
+ * @param fastifyInstance - the server instance to register the routes on.
480
+ */
481
+ registerImageRoutes(fastifyInstance?: FastifyInstance): Promise<void>;
482
+ /**
483
+ * Register the dynamic data routes.
484
+ * @param fastifyInstance - the server instance to register the routes on.
485
+ */
486
+ registerDynamicDataRoutes(fastifyInstance?: FastifyInstance): Promise<void>;
487
+ private resolveHttpsCredentials;
488
+ private loadPemValue;
489
+ }
490
+ //#endregion
491
+ //#region src/index.d.ts
492
+ declare const start: () => Promise<MockHttp>;
493
+ //#endregion
494
+ export { type CertificateFileOptions, type CertificateOptions, type CertificateResult, type HttpsOptions, type InjectionMatcher, type InjectionResponse, type InjectionTap, MockHttp as default, MockHttp as mockhttp, generateCertificate, generateCertificateFiles, start };