msw-fetch-mock 0.1.2 → 0.2.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
@@ -23,7 +23,7 @@ npm install -D msw-fetch-mock msw
23
23
  ```typescript
24
24
  import { fetchMock } from 'msw-fetch-mock';
25
25
 
26
- beforeAll(() => fetchMock.activate());
26
+ beforeAll(() => fetchMock.activate({ onUnhandledRequest: 'error' }));
27
27
  afterAll(() => fetchMock.deactivate());
28
28
  afterEach(() => fetchMock.assertNoPendingInterceptors());
29
29
 
@@ -62,6 +62,32 @@ afterEach(() => {
62
62
 
63
63
  > **Note:** Only one MSW server can be active at a time. If another server is already listening, standalone `activate()` will throw an error guiding you to use `new FetchMock(server)` instead.
64
64
 
65
+ ## Unhandled Requests
66
+
67
+ By default `activate()` uses `'error'` mode — unmatched requests cause `fetch()` to reject. This includes requests to **consumed** interceptors (once a one-shot interceptor has been used, its handler is removed from MSW).
68
+
69
+ ```typescript
70
+ // Reject unmatched requests (default)
71
+ fetchMock.activate();
72
+ fetchMock.activate({ onUnhandledRequest: 'error' });
73
+
74
+ // Log a warning but allow passthrough
75
+ fetchMock.activate({ onUnhandledRequest: 'warn' });
76
+
77
+ // Silently allow passthrough
78
+ fetchMock.activate({ onUnhandledRequest: 'bypass' });
79
+
80
+ // Custom callback
81
+ fetchMock.activate({
82
+ onUnhandledRequest: (request, print) => {
83
+ if (request.url.includes('/health')) return; // ignore
84
+ print.error(); // block everything else
85
+ },
86
+ });
87
+ ```
88
+
89
+ > `enableNetConnect()` takes priority over `onUnhandledRequest` — allowed hosts always pass through.
90
+
65
91
  ## API Overview
66
92
 
67
93
  ### `fetchMock` (singleton)
package/dist/index.d.ts CHANGED
@@ -2,7 +2,7 @@ import { FetchMock } from './mock-server';
2
2
  export { createFetchMock, FetchMock } from './mock-server';
3
3
  /** Pre-built singleton for quick standalone use (Cloudflare migration compatible). */
4
4
  export declare const fetchMock: FetchMock;
5
- export type { InterceptOptions, MockPool, MockInterceptor, MockReplyChain, ReplyOptions, PendingInterceptor, } from './mock-server';
5
+ export type { ActivateOptions, OnUnhandledRequest, InterceptOptions, MockPool, MockInterceptor, MockReplyChain, ReplyOptions, PendingInterceptor, } from './mock-server';
6
6
  export { MockCallHistory, MockCallHistoryLog } from './mock-call-history';
7
7
  export type { MockCallHistoryLogData, CallHistoryFilterCriteria } from './mock-call-history';
8
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE1C,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE3D,sFAAsF;AACtF,eAAO,MAAM,SAAS,WAAkB,CAAC;AACzC,YAAY,EACV,gBAAgB,EAChB,QAAQ,EACR,eAAe,EACf,cAAc,EACd,YAAY,EACZ,kBAAkB,GACnB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAC1E,YAAY,EAAE,sBAAsB,EAAE,yBAAyB,EAAE,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE1C,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE3D,sFAAsF;AACtF,eAAO,MAAM,SAAS,WAAkB,CAAC;AACzC,YAAY,EACV,eAAe,EACf,kBAAkB,EAClB,gBAAgB,EAChB,QAAQ,EACR,eAAe,EACf,cAAc,EACd,YAAY,EACZ,kBAAkB,GACnB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAC1E,YAAY,EAAE,sBAAsB,EAAE,yBAAyB,EAAE,MAAM,qBAAqB,CAAC"}
@@ -45,18 +45,33 @@ export interface PendingInterceptor {
45
45
  timesInvoked: number;
46
46
  persist: boolean;
47
47
  }
48
+ type PrintAPI = {
49
+ warning(): void;
50
+ error(): void;
51
+ };
52
+ type OnUnhandledRequestCallback = (request: Request, print: PrintAPI) => void;
53
+ export type OnUnhandledRequest = 'bypass' | 'warn' | 'error' | OnUnhandledRequestCallback;
54
+ export interface ActivateOptions {
55
+ onUnhandledRequest?: OnUnhandledRequest;
56
+ }
48
57
  export declare class FetchMock {
49
58
  private readonly _calls;
50
59
  private server;
51
60
  private readonly ownsServer;
52
61
  private interceptors;
53
62
  private netConnectAllowed;
63
+ private mswHandlers;
54
64
  get calls(): MockCallHistory;
55
65
  constructor(externalServer?: SetupServerLike);
56
- activate(): void;
66
+ activate(options?: ActivateOptions): void;
57
67
  disableNetConnect(): void;
58
68
  enableNetConnect(matcher?: string | RegExp | ((host: string) => boolean)): void;
59
69
  private isNetConnectAllowed;
70
+ /**
71
+ * Remove consumed MSW handlers so future requests to those URLs
72
+ * go through MSW's onUnhandledRequest instead of silently passing through.
73
+ */
74
+ private syncMswHandlers;
60
75
  getCallHistory(): MockCallHistory;
61
76
  clearCallHistory(): void;
62
77
  deactivate(): void;
@@ -1 +1 @@
1
- {"version":3,"file":"mock-server.d.ts","sourceRoot":"","sources":["../src/mock-server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"mock-server.d.ts","sourceRoot":"","sources":["../src/mock-server.ts"],"names":[],"mappings":"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"}
@@ -99,6 +99,8 @@ export class FetchMock {
99
99
  ownsServer;
100
100
  interceptors = [];
101
101
  netConnectAllowed = false;
102
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
103
+ mswHandlers = new Map();
102
104
  get calls() {
103
105
  return this._calls;
104
106
  }
@@ -106,19 +108,29 @@ export class FetchMock {
106
108
  this.server = externalServer ?? null;
107
109
  this.ownsServer = !externalServer;
108
110
  }
109
- activate() {
111
+ activate(options) {
110
112
  if (this.ownsServer) {
111
113
  const isPatched = Object.getOwnPropertySymbols(globalThis.fetch).some((s) => s.description === 'isPatchedModule');
112
114
  if (isPatched) {
113
115
  throw new Error('Another MSW server is already active. ' +
114
116
  'Pass your existing server to new FetchMock(server) instead.');
115
117
  }
118
+ const mode = options?.onUnhandledRequest ?? 'error';
116
119
  this.server = setupServer();
117
120
  this.server.listen({
118
121
  onUnhandledRequest: (request, print) => {
119
122
  if (this.isNetConnectAllowed(request))
120
123
  return;
121
- print.error();
124
+ if (typeof mode === 'function') {
125
+ mode(request, print);
126
+ }
127
+ else if (mode === 'error') {
128
+ print.error();
129
+ }
130
+ else if (mode === 'warn') {
131
+ print.warning();
132
+ }
133
+ // 'bypass' → do nothing
122
134
  },
123
135
  });
124
136
  }
@@ -141,6 +153,18 @@ export class FetchMock {
141
153
  return this.netConnectAllowed.test(host);
142
154
  return this.netConnectAllowed(host);
143
155
  }
156
+ /**
157
+ * Remove consumed MSW handlers so future requests to those URLs
158
+ * go through MSW's onUnhandledRequest instead of silently passing through.
159
+ */
160
+ syncMswHandlers() {
161
+ if (!this.server || !this.ownsServer)
162
+ return;
163
+ const activeHandlers = [...this.mswHandlers.entries()]
164
+ .filter(([p]) => !p.consumed || p.persist)
165
+ .map(([, handler]) => handler);
166
+ this.server.resetHandlers(...activeHandlers);
167
+ }
144
168
  getCallHistory() {
145
169
  return this._calls;
146
170
  }
@@ -149,6 +173,7 @@ export class FetchMock {
149
173
  }
150
174
  deactivate() {
151
175
  this.interceptors = [];
176
+ this.mswHandlers.clear();
152
177
  this._calls.clear();
153
178
  if (this.ownsServer) {
154
179
  this.server?.close();
@@ -158,6 +183,7 @@ export class FetchMock {
158
183
  assertNoPendingInterceptors() {
159
184
  const unconsumed = this.interceptors.filter(isPending);
160
185
  this.interceptors = [];
186
+ this.mswHandlers.clear();
161
187
  this._calls.clear();
162
188
  if (this.ownsServer) {
163
189
  this.server?.resetHandlers();
@@ -207,6 +233,7 @@ export class FetchMock {
207
233
  pending.timesInvoked++;
208
234
  if (!pending.persist && pending.timesInvoked >= pending.times) {
209
235
  pending.consumed = true;
236
+ this.syncMswHandlers();
210
237
  }
211
238
  recordCall(this._calls, request, bodyText);
212
239
  return bodyText;
@@ -216,6 +243,7 @@ export class FetchMock {
216
243
  if (!this.server) {
217
244
  throw new Error('FetchMock server is not active. Call activate() before registering interceptors.');
218
245
  }
246
+ this.mswHandlers.set(pending, handler);
219
247
  this.server.use(handler);
220
248
  };
221
249
  const buildChain = (delayRef) => ({
package/docs/api.md CHANGED
@@ -7,7 +7,7 @@ A pre-built `FetchMock` instance for standalone use. No setup required — just
7
7
  ```typescript
8
8
  import { fetchMock } from 'msw-fetch-mock';
9
9
 
10
- beforeAll(() => fetchMock.activate());
10
+ beforeAll(() => fetchMock.activate({ onUnhandledRequest: 'error' }));
11
11
  afterAll(() => fetchMock.deactivate());
12
12
  afterEach(() => fetchMock.assertNoPendingInterceptors());
13
13
  ```
@@ -41,14 +41,47 @@ const fetchMock = new FetchMock(server);
41
41
  ### Lifecycle
42
42
 
43
43
  ```typescript
44
- fetchMock.activate(); // start intercepting (calls server.listen())
45
- fetchMock.deactivate(); // stop intercepting (calls server.close())
44
+ fetchMock.activate(options?); // start intercepting (calls server.listen())
45
+ fetchMock.deactivate(); // stop intercepting (calls server.close())
46
46
  ```
47
47
 
48
48
  > If you pass an external server that you manage yourself, `activate()` / `deactivate()` are no-ops.
49
49
  >
50
50
  > **Conflict detection:** In standalone mode, `activate()` checks whether `globalThis.fetch` is already patched by MSW. If so, it throws an error guiding you to pass your existing server via `new FetchMock(server)` instead.
51
51
 
52
+ #### `ActivateOptions`
53
+
54
+ | Property | Type | Default | Description |
55
+ | -------------------- | -------------------- | --------- | --------------------------------------------------- |
56
+ | `onUnhandledRequest` | `OnUnhandledRequest` | `'error'` | How to handle requests with no matching interceptor |
57
+
58
+ #### `OnUnhandledRequest`
59
+
60
+ | Value | Behavior |
61
+ | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
62
+ | `'error'` | MSW prints an error and `fetch()` rejects with an `InternalError` |
63
+ | `'warn'` | MSW prints a warning; the request passes through to the real network |
64
+ | `'bypass'` | Silently passes through to the real network |
65
+ | `(request, print) => void` | Custom callback. Call `print.error()` to block or `print.warning()` to warn. Return without calling either to silently bypass. |
66
+
67
+ ```typescript
68
+ // Default — reject unmatched requests
69
+ fetchMock.activate();
70
+ fetchMock.activate({ onUnhandledRequest: 'error' });
71
+
72
+ // Custom callback
73
+ fetchMock.activate({
74
+ onUnhandledRequest: (request, print) => {
75
+ if (new URL(request.url).pathname === '/health') return;
76
+ print.error();
77
+ },
78
+ });
79
+ ```
80
+
81
+ > **Consumed interceptors:** Once a one-shot interceptor has been fully consumed, its MSW handler is removed. Subsequent requests to that URL are treated as unhandled and go through `onUnhandledRequest`. This prevents consumed interceptors from silently passing through.
82
+ >
83
+ > **Priority:** `enableNetConnect()` takes priority over `onUnhandledRequest` — allowed hosts always pass through regardless of the unhandled request mode.
84
+
52
85
  ### `fetchMock.calls`
53
86
 
54
87
  Returns the `MockCallHistory` instance for inspecting and managing recorded requests.
@@ -63,13 +63,13 @@ it('calls API', async () => {
63
63
 
64
64
  ## Key Differences
65
65
 
66
- | Aspect | cloudflare:test | msw-fetch-mock |
67
- | -------------------- | --------------------------------------------- | ------------------------------------------------- |
68
- | Import | `import { fetchMock } from 'cloudflare:test'` | `import { fetchMock } from 'msw-fetch-mock'` |
69
- | Server lifecycle | Implicit (managed by test framework) | Explicit (`activate()` / `deactivate()`) |
70
- | Call history access | `fetchMock.getCallHistory()` | `fetchMock.getCallHistory()` or `fetchMock.calls` |
71
- | Call history cleanup | Automatic per test | Automatic via `assertNoPendingInterceptors()` |
72
- | Network connect | Must call `disableNetConnect()` | MSW blocks unhandled requests by default |
73
- | Runtime | Cloudflare Workers (workerd) | Node.js |
66
+ | Aspect | cloudflare:test | msw-fetch-mock |
67
+ | -------------------- | --------------------------------------------- | -------------------------------------------------- |
68
+ | Import | `import { fetchMock } from 'cloudflare:test'` | `import { fetchMock } from 'msw-fetch-mock'` |
69
+ | Server lifecycle | Implicit (managed by test framework) | Explicit (`activate()` / `deactivate()`) |
70
+ | Call history access | `fetchMock.getCallHistory()` | `fetchMock.getCallHistory()` or `fetchMock.calls` |
71
+ | Call history cleanup | Automatic per test | Automatic via `assertNoPendingInterceptors()` |
72
+ | Unhandled requests | Must call `disableNetConnect()` | `onUnhandledRequest: 'error'` by default (rejects) |
73
+ | Runtime | Cloudflare Workers (workerd) | Node.js |
74
74
 
75
75
  > **Note:** `getCallHistory()` and `clearCallHistory()` are provided as Cloudflare-compatible aliases. You can use either the Cloudflare-style methods or the `fetchMock.calls` getter — they are equivalent.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "msw-fetch-mock",
3
- "version": "0.1.2",
3
+ "version": "0.2.0",
4
4
  "description": "Undici-style fetch mock API built on MSW (Mock Service Worker)",
5
5
  "type": "module",
6
6
  "license": "MIT",