@jaredwray/mockhttp 2.0.0 → 3.0.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 +68 -68
- package/dist/index.d.mts +130 -159
- package/dist/index.mjs +200 -231
- package/package.json +21 -18
- package/site/dist/api/index.html +5 -5
- package/site/dist/api/swagger.json +10 -10
- package/site/dist/changelog/index.html +49 -33
- package/site/dist/changelog/page/2/index.html +33 -32
- package/site/dist/changelog/{v0-2-0 → v2-1-0}/index.html +47 -8
- package/site/dist/changelog/{v0-1-0 → v3-0-0}/index.html +71 -22
- package/site/dist/changelog-latest.json +26 -26
- package/site/dist/changelog.json +26 -26
- package/site/dist/docs/bins/index.html +78 -53
- package/site/dist/docs/configuration/index.html +42 -4
- package/site/dist/docs/hosted/index.html +47 -8
- package/site/dist/docs/how-to-deploy/cloudflare/index.html +650 -0
- package/site/dist/docs/how-to-deploy/docker/index.html +664 -0
- package/site/dist/docs/how-to-deploy/wasmer/index.html +625 -0
- package/site/dist/docs/https/index.html +42 -4
- package/site/dist/docs/index.html +44 -5
- package/site/dist/docs/library/index.html +58 -24
- package/site/dist/docs/taps/index.html +42 -4
- package/site/dist/feed.xml +1 -1
- package/site/dist/index.html +44 -5
- package/site/dist/llms-full.txt +390 -115
- package/site/dist/llms.txt +6 -3
- package/site/dist/search-index.json +1 -1
- package/site/dist/sitemap.xml +1 -1
package/README.md
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
[](https://npmjs.com/package/@jaredwray/mockhttp)
|
|
8
8
|
[](https://hub.docker.com/r/jaredwray/mockhttp)
|
|
9
9
|
[](https://mockhttp.org)
|
|
10
|
+
[](https://drydock.org/diff/@jaredwray/mockhttp)
|
|
10
11
|
|
|
11
12
|
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
13
|
|
|
@@ -16,7 +17,7 @@ A simple HTTP server that can be used to mock HTTP responses for testing purpose
|
|
|
16
17
|
* Bins - Capture and inspect incoming HTTP requests (great for webhook debugging)
|
|
17
18
|
* `@fastify/helmet` built in by default
|
|
18
19
|
* Built with `nodejs`, `typescript`, and `fastify`
|
|
19
|
-
* Deploy via `docker` or
|
|
20
|
+
* Deploy via `docker`, `nodejs`, or Wasmer Edge
|
|
20
21
|
* Free hosted service at [mockhttp.org](https://mockhttp.org), running on a Cloudflare Worker
|
|
21
22
|
* Documentation site and interactive OpenAPI reference (Docula)
|
|
22
23
|
* Auto detect the port that is not in use
|
|
@@ -26,6 +27,7 @@ A simple HTTP server that can be used to mock HTTP responses for testing purpose
|
|
|
26
27
|
- [Deploy via Docker](#deploy-via-docker)
|
|
27
28
|
- [Deploy via Docker Compose](#deploy-via-docker-compose)
|
|
28
29
|
- [Deploy via NodeJS](#deploy-via-nodejs)
|
|
30
|
+
- [Deploy via Wasmer](#deploy-via-wasmer)
|
|
29
31
|
- [HTTPS Support](#https-support)
|
|
30
32
|
- [HTTP/2 Support](#http2-support)
|
|
31
33
|
- [Response Injection (Tap Feature)](#response-injection-tap-feature)
|
|
@@ -82,6 +84,12 @@ console.log(response);
|
|
|
82
84
|
await mock.close(); // stop the server
|
|
83
85
|
```
|
|
84
86
|
|
|
87
|
+
# Deploy via Wasmer
|
|
88
|
+
|
|
89
|
+
Wasmer Edge runs the same Node server as Docker. [deploy-wasmer.yaml](.github/workflows/deploy-wasmer.yaml) publishes the source with `wasmer deploy --build-remote`, and the `Anybuild` file builds the server and the docs site. It runs when you start it by hand and when a GitHub Release is published. It does not deploy the Cloudflare Worker. The test app is [mockhttp.wasmer.app](https://mockhttp.wasmer.app).
|
|
90
|
+
|
|
91
|
+
Bins, taps, and `@fastify/rate-limit` are per instance on Wasmer. All [mockhttp.org](https://mockhttp.org) traffic stays on Cloudflare, where the rate limiter is shared across requests.
|
|
92
|
+
|
|
85
93
|
# HTTPS Support
|
|
86
94
|
|
|
87
95
|
MockHttp supports HTTPS with auto-generated self-signed certificates or your own custom certificates. No external dependencies are required — certificate generation uses only Node.js built-in `crypto`.
|
|
@@ -610,13 +618,13 @@ curl -s "http://localhost:3000/bins/$ID/requests" \
|
|
|
610
618
|
|
|
611
619
|
### Truncation
|
|
612
620
|
|
|
613
|
-
Bodies larger than `maxBodySize` (
|
|
621
|
+
Bodies larger than `maxBodySize` (128 KiB by default) are cut off and flagged:
|
|
614
622
|
|
|
615
623
|
```javascript
|
|
616
624
|
// In test setup
|
|
617
|
-
import { MockHttp,
|
|
625
|
+
import { MockHttp, MockHttpBins } from '@jaredwray/mockhttp';
|
|
618
626
|
const mock = new MockHttp();
|
|
619
|
-
mock.bins = new
|
|
627
|
+
mock.bins = new MockHttpBins({ maxBodySize: 1024 }); // 1 KiB cap
|
|
620
628
|
await mock.start();
|
|
621
629
|
```
|
|
622
630
|
|
|
@@ -656,8 +664,7 @@ curl -s "http://localhost:3000/bins/$ID/requests" | jq '.requests[0].path'
|
|
|
656
664
|
|
|
657
665
|
## Programmatic Access
|
|
658
666
|
|
|
659
|
-
|
|
660
|
-
makes it easy to drive bins from a test suite without going through HTTP:
|
|
667
|
+
Use `mock.bins` to control request bins from a test. You do not need an HTTP call:
|
|
661
668
|
|
|
662
669
|
```javascript
|
|
663
670
|
import { MockHttp } from '@jaredwray/mockhttp';
|
|
@@ -666,13 +673,13 @@ const mock = new MockHttp({ logging: false });
|
|
|
666
673
|
await mock.start();
|
|
667
674
|
|
|
668
675
|
// Create a bin
|
|
669
|
-
const bin = mock.bins.createBin();
|
|
676
|
+
const bin = await mock.bins.createBin();
|
|
670
677
|
console.log(`Webhook target: http://localhost:${mock.port}/b/${bin.id}`);
|
|
671
678
|
|
|
672
679
|
// ... point your code-under-test at that URL ...
|
|
673
680
|
|
|
674
681
|
// Read captured requests (newest first)
|
|
675
|
-
const requests = mock.bins.getRequests(bin.id);
|
|
682
|
+
const requests = await mock.bins.getRequests(bin.id);
|
|
676
683
|
console.log(`Captured ${requests.length} requests`);
|
|
677
684
|
|
|
678
685
|
for (const req of requests) {
|
|
@@ -680,8 +687,8 @@ for (const req of requests) {
|
|
|
680
687
|
}
|
|
681
688
|
|
|
682
689
|
// Clean up
|
|
683
|
-
mock.bins.deleteBin(bin.id);
|
|
684
|
-
await mock.close();
|
|
690
|
+
await mock.bins.deleteBin(bin.id);
|
|
691
|
+
await mock.close();
|
|
685
692
|
```
|
|
686
693
|
|
|
687
694
|
### Using Bins in a Vitest Test
|
|
@@ -702,14 +709,14 @@ afterAll(async () => {
|
|
|
702
709
|
});
|
|
703
710
|
|
|
704
711
|
test('my SDK sends the right webhook payload', async () => {
|
|
705
|
-
const bin = mock.bins.createBin();
|
|
712
|
+
const bin = await mock.bins.createBin();
|
|
706
713
|
const webhookUrl = `http://localhost:${mock.port}/b/${bin.id}`;
|
|
707
714
|
|
|
708
715
|
// Drive your SDK at the bin
|
|
709
716
|
await mySdk.notify(webhookUrl, { event: 'user.created', id: 42 });
|
|
710
717
|
|
|
711
718
|
// Assert on what actually arrived
|
|
712
|
-
const [captured] = mock.bins.getRequests(bin.id);
|
|
719
|
+
const [captured] = await mock.bins.getRequests(bin.id);
|
|
713
720
|
expect(captured.method).toBe('POST');
|
|
714
721
|
expect(captured.contentType).toBe('application/json');
|
|
715
722
|
expect(JSON.parse(captured.body!)).toEqual({
|
|
@@ -730,18 +737,17 @@ const mock = new MockHttp({
|
|
|
730
737
|
});
|
|
731
738
|
```
|
|
732
739
|
|
|
733
|
-
To
|
|
740
|
+
To change the limits, replace the default `MockHttpBins` before you start the server:
|
|
734
741
|
|
|
735
742
|
```javascript
|
|
736
|
-
import { MockHttp,
|
|
743
|
+
import { MockHttp, MockHttpBins } from '@jaredwray/mockhttp';
|
|
737
744
|
|
|
738
745
|
const mock = new MockHttp();
|
|
739
|
-
mock.bins = new
|
|
746
|
+
mock.bins = new MockHttpBins({
|
|
740
747
|
defaultTtlMs: 60 * 60 * 1000, // 1 hour (default: 24h)
|
|
741
748
|
maxRequestsPerBin: 500, // (default: 100)
|
|
742
|
-
maxBodySize:
|
|
749
|
+
maxBodySize: 512 * 1024, // 512 KiB (default: 128 KiB)
|
|
743
750
|
idLength: 16, // (default: 12)
|
|
744
|
-
cleanupIntervalMs: 30 * 1000, // sweep every 30s (default: 60s)
|
|
745
751
|
});
|
|
746
752
|
|
|
747
753
|
await mock.start();
|
|
@@ -751,44 +757,40 @@ await mock.start();
|
|
|
751
757
|
|
|
752
758
|
| Option | Default | Description |
|
|
753
759
|
|--------|---------|-------------|
|
|
754
|
-
| `defaultTtlMs` | `86400000` (24h) | Bin lifetime
|
|
760
|
+
| `defaultTtlMs` | `86400000` (24h) | Bin lifetime, set as the Keyv TTL. An expired bin returns 404. |
|
|
755
761
|
| `maxRequestsPerBin` | `100` | When exceeded, oldest captures are dropped (FIFO). |
|
|
756
|
-
| `maxBodySize` | `
|
|
762
|
+
| `maxBodySize` | `131072` (128 KiB) | Larger bodies are truncated; `truncated: true` is set on the capture. |
|
|
757
763
|
| `idLength` | `12` | Length of generated bin and request ids. |
|
|
758
|
-
| `
|
|
764
|
+
| `keyv` | `new Keyv()` | Keyv store for bins. The default keeps bins in memory. |
|
|
765
|
+
|
|
766
|
+
### Storage
|
|
759
767
|
|
|
760
|
-
|
|
768
|
+
`MockHttpBins` stores each bin in [Keyv](https://github.com/jaredwray/keyv) 6. The default is `new Keyv()`. That store keeps the data in memory for this process.
|
|
761
769
|
|
|
762
|
-
|
|
763
|
-
backends (Redis, SQLite, etc.) without changing the rest of the codebase. The
|
|
764
|
-
default `InMemoryBinStore` keeps state in process memory.
|
|
770
|
+
Pass a Keyv 6 instance when you want a different adapter. Install the adapter in your project. This package does not include an adapter. `@keyv/redis` 6 matches this Keyv version.
|
|
765
771
|
|
|
766
772
|
```typescript
|
|
767
|
-
import
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
type Bin,
|
|
771
|
-
type BinStore,
|
|
772
|
-
type CapturedRequest,
|
|
773
|
-
} from '@jaredwray/mockhttp';
|
|
774
|
-
|
|
775
|
-
class RedisBinStore implements BinStore {
|
|
776
|
-
createBin(bin: Bin): void { /* SET bin:${bin.id} ... */ }
|
|
777
|
-
getBin(id: string): Bin | undefined { /* GET ... */ }
|
|
778
|
-
listBins(): Bin[] { /* SCAN ... */ }
|
|
779
|
-
deleteBin(id: string): boolean { /* DEL ... */ }
|
|
780
|
-
addRequest(binId: string, req: CapturedRequest, max: number): void {
|
|
781
|
-
/* LPUSH bin:${binId}:requests + LTRIM to max */
|
|
782
|
-
}
|
|
783
|
-
getRequests(binId: string): CapturedRequest[] { /* LRANGE ... */ }
|
|
784
|
-
getRequest(binId: string, reqId: string): CapturedRequest | undefined { /* ... */ }
|
|
785
|
-
clearRequests(binId: string): void { /* DEL bin:${binId}:requests */ }
|
|
786
|
-
cleanupExpired(now: number): string[] { /* scan + delete */ return []; }
|
|
787
|
-
}
|
|
773
|
+
import Keyv from 'keyv';
|
|
774
|
+
import KeyvRedis from '@keyv/redis'; // pnpm add @keyv/redis
|
|
775
|
+
import { MockHttpBins } from '@jaredwray/mockhttp';
|
|
788
776
|
|
|
789
|
-
mock.bins = new
|
|
777
|
+
mock.bins = new MockHttpBins({
|
|
778
|
+
keyv: new Keyv({ store: new KeyvRedis('redis://localhost:6379') }),
|
|
779
|
+
});
|
|
790
780
|
```
|
|
791
781
|
|
|
782
|
+
Each capture writes the full bin again. A large body or a high request limit makes that write large. Set `maxBodySize` and `maxRequestsPerBin` to limit the size. With the defaults, a full bin of binary bodies is about 17 MB.
|
|
783
|
+
|
|
784
|
+
Two processes that share one Keyv can lose a capture. This occurs when both processes write the same bin at the same time.
|
|
785
|
+
|
|
786
|
+
If Keyv does not store a write, the bin route returns `500` with the standard Fastify error body: `statusCode`, `error`, and `message`.
|
|
787
|
+
|
|
788
|
+
A failed read depends on the Keyv `error` listener. Without a listener, Keyv throws the store error and the route returns `500`. With a listener, Keyv does not throw. A failed read then looks like a missing bin, and the route returns `404`. `listBins()` returns only the bins that it read before the error.
|
|
789
|
+
|
|
790
|
+
`listBins()` throws an error when the adapter has no iterator.
|
|
791
|
+
|
|
792
|
+
An expired bin returns 404. The in-memory Keyv store deletes an expired bin only when a read or `listBins()` reaches it. Until then, the bin stays in memory.
|
|
793
|
+
|
|
792
794
|
# Rate Limiting
|
|
793
795
|
|
|
794
796
|
MockHttp supports rate limiting using [@fastify/rate-limit](https://github.com/fastify/fastify-rate-limit). Rate limiting is **enabled by default** at **1000 requests per minute** with **localhost (127.0.0.1 and ::1) excluded** from rate limiting.
|
|
@@ -1004,7 +1006,6 @@ new MockHttp(options?)
|
|
|
1004
1006
|
- `logging?`: boolean - Enable logging (default: true)
|
|
1005
1007
|
- `staticFiles?`: boolean - Serve files from the package `public/` directory (default: true)
|
|
1006
1008
|
- `pluginTimeout?`: number - Fastify plugin boot timeout in milliseconds; `0` disables it (default: Fastify's timeout)
|
|
1007
|
-
- `startBins?`: boolean - Start the request-bin cleanup timer during `initialize()` (default: true)
|
|
1008
1009
|
- `httpBin?`: HttpBinOptions - Configure which httpbin routes to enable
|
|
1009
1010
|
- `httpMethods?`: boolean - Enable HTTP method routes (default: true)
|
|
1010
1011
|
- `redirects?`: boolean - Enable redirect routes (default: true)
|
|
@@ -1033,7 +1034,6 @@ new MockHttp(options?)
|
|
|
1033
1034
|
- `logging`: boolean - Get/set logging enabled state
|
|
1034
1035
|
- `staticFiles`: boolean - Get/set whether files from `public/` are served
|
|
1035
1036
|
- `pluginTimeout`: number | undefined - Get/set Fastify plugin boot timeout
|
|
1036
|
-
- `startBins`: boolean - Get/set whether bin cleanup starts during `initialize()`
|
|
1037
1037
|
- `rateLimit`: RateLimitPluginOptions | undefined - Get/set rate limiting options
|
|
1038
1038
|
- `httpBin`: HttpBinOptions - Get/set httpbin route options
|
|
1039
1039
|
- `https`: HttpsOptions | undefined - Get/set HTTPS configuration
|
|
@@ -1042,7 +1042,7 @@ new MockHttp(options?)
|
|
|
1042
1042
|
- `http1`: boolean - Get/set HTTP/1.1 fallback for HTTP/2 with HTTPS
|
|
1043
1043
|
- `server`: FastifyInstance - Get/set the Fastify server instance
|
|
1044
1044
|
- `taps`: TapManager - Get/set the TapManager instance
|
|
1045
|
-
- `bins`:
|
|
1045
|
+
- `bins`: MockHttpBins - Get or set the request bins
|
|
1046
1046
|
|
|
1047
1047
|
### Methods
|
|
1048
1048
|
|
|
@@ -1122,57 +1122,55 @@ Register the request bin routes — management at `/bins` and capture at `/b/:id
|
|
|
1122
1122
|
|
|
1123
1123
|
## Bins (Request Capture)
|
|
1124
1124
|
|
|
1125
|
-
|
|
1125
|
+
Use `mockHttp.bins` to control request bins. See [Request Bins](#request-bins) for usage examples.
|
|
1126
1126
|
|
|
1127
1127
|
### `bins.createBin()`
|
|
1128
1128
|
|
|
1129
|
-
Create a new bin with the configured TTL.
|
|
1129
|
+
Create a new bin with the configured TTL. Throws when Keyv does not store the bin.
|
|
1130
1130
|
|
|
1131
|
-
**Returns:** `Bin
|
|
1131
|
+
**Returns:** `Promise<Bin>` — `{ id, createdAt, expiresAt, requestCount }`
|
|
1132
1132
|
|
|
1133
1133
|
### `bins.getBin(id)`
|
|
1134
1134
|
|
|
1135
|
-
Look up a bin by id. Returns `undefined` if the bin does not exist or has expired
|
|
1135
|
+
Look up a bin by id. Returns `undefined` if the bin does not exist or Keyv has expired it. When the Keyv instance has an `error` listener, a failed read also returns `undefined`.
|
|
1136
1136
|
|
|
1137
|
-
**Returns:** `Bin | undefined
|
|
1137
|
+
**Returns:** `Promise<Bin | undefined>`
|
|
1138
1138
|
|
|
1139
1139
|
### `bins.listBins()`
|
|
1140
1140
|
|
|
1141
|
-
List
|
|
1141
|
+
List the bins that Keyv still holds.
|
|
1142
1142
|
|
|
1143
|
-
**Returns:** `Bin[]
|
|
1143
|
+
**Returns:** `Promise<Bin[]>`
|
|
1144
1144
|
|
|
1145
1145
|
### `bins.deleteBin(id)`
|
|
1146
1146
|
|
|
1147
|
-
Delete a bin and all its captured requests.
|
|
1147
|
+
Delete a bin and all its captured requests. The delete waits for captures already queued for that bin.
|
|
1148
1148
|
|
|
1149
|
-
**Returns:** `boolean
|
|
1149
|
+
**Returns:** `Promise<boolean>` — `true` when the bin existed
|
|
1150
1150
|
|
|
1151
1151
|
### `bins.getRequests(binId)`
|
|
1152
1152
|
|
|
1153
1153
|
List captured requests for a bin, newest first.
|
|
1154
1154
|
|
|
1155
|
-
**Returns:** `CapturedRequest[]
|
|
1155
|
+
**Returns:** `Promise<CapturedRequest[]>`
|
|
1156
1156
|
|
|
1157
1157
|
### `bins.getRequest(binId, reqId)`
|
|
1158
1158
|
|
|
1159
1159
|
Look up a single captured request.
|
|
1160
1160
|
|
|
1161
|
-
**Returns:** `CapturedRequest | undefined
|
|
1161
|
+
**Returns:** `Promise<CapturedRequest | undefined>`
|
|
1162
1162
|
|
|
1163
1163
|
### `bins.clearRequests(binId)`
|
|
1164
1164
|
|
|
1165
|
-
Clear all captured requests in a bin
|
|
1165
|
+
Clear all captured requests in a bin. The bin stays. Throws when Keyv does not store the change.
|
|
1166
1166
|
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
Manually record a captured request. Returns `undefined` if the bin does not exist or is expired. Normally invoked by the capture route, but available for programmatic use.
|
|
1167
|
+
**Returns:** `Promise<boolean>` — `false` when the bin does not exist
|
|
1170
1168
|
|
|
1171
|
-
|
|
1169
|
+
### `bins.recordRequest(binId, raw)`
|
|
1172
1170
|
|
|
1173
|
-
|
|
1171
|
+
Manually record a captured request. Returns `undefined` if the bin does not exist. Throws when Keyv does not store the capture. Normally invoked by the capture route, but available for programmatic use.
|
|
1174
1172
|
|
|
1175
|
-
|
|
1173
|
+
**Returns:** `Promise<CapturedRequest | undefined>`
|
|
1176
1174
|
|
|
1177
1175
|
### `bins.maxBodySize` / `bins.maxRequestsPerBin` / `bins.defaultTtlMs`
|
|
1178
1176
|
|
|
@@ -1232,7 +1230,9 @@ A getter that returns whether there are any active injections.
|
|
|
1232
1230
|
|
|
1233
1231
|
# About mockhttp.org
|
|
1234
1232
|
|
|
1235
|
-
[mockhttp.org](https://mockhttp.org) is a free hosted instance of this codebase for testing. It runs on a [Cloudflare Worker](https://developers.cloudflare.com/workers/) (no containers): documentation is served from Workers Assets, and mock APIs such as `/get` and `/post` run in the Worker. The service is globally available and rate-limited (1000 requests per minute per IP) to prevent abuse.
|
|
1233
|
+
[mockhttp.org](https://mockhttp.org) is a free hosted instance of this codebase for testing. It runs on a [Cloudflare Worker](https://developers.cloudflare.com/workers/) (no containers): documentation is served from Workers Assets, and mock APIs such as `/get` and `/post` run in the Worker. The service is globally available and rate-limited (1000 requests per minute per IP) to prevent abuse. Request bins on this service are stored in MongoDB Atlas, in the `bins` collection, so they are shared across isolates.
|
|
1234
|
+
|
|
1235
|
+
All [mockhttp.org](https://mockhttp.org) traffic stays on Cloudflare. A separate test app runs the Node server on Wasmer Edge at [mockhttp.wasmer.app](https://mockhttp.wasmer.app).
|
|
1236
1236
|
|
|
1237
1237
|
Getting Started is served at `/` and `/docs`, other guides at `/docs/...`, and the interactive HTTP API reference at `/api`. Mock endpoints such as `/get` and `/post` are unchanged.
|
|
1238
1238
|
|