@zimic/interceptor 1.2.7-canary.2 → 1.3.0-canary.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/dist/{chunk-6GEP6R3L.mjs → chunk-4L2JH2L4.mjs} +80 -23
- package/dist/chunk-4L2JH2L4.mjs.map +1 -0
- package/dist/{chunk-PB4TJVK3.js → chunk-F5OGZSHS.js} +80 -23
- package/dist/chunk-F5OGZSHS.js.map +1 -0
- package/dist/cli.js +17 -19
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +2 -4
- package/dist/cli.mjs.map +1 -1
- package/dist/http.d.ts +95 -6
- package/dist/http.js +253 -186
- package/dist/http.js.map +1 -1
- package/dist/http.mjs +254 -187
- package/dist/http.mjs.map +1 -1
- package/dist/server.js +6 -6
- package/dist/server.mjs +1 -1
- package/package.json +2 -2
- package/src/cli/server/start.ts +7 -2
- package/src/http/interceptor/HttpInterceptorClient.ts +8 -10
- package/src/http/interceptor/types/options.ts +11 -5
- package/src/http/interceptorWorker/HttpInterceptorWorker.ts +73 -15
- package/src/http/interceptorWorker/LocalHttpInterceptorWorker.ts +36 -11
- package/src/http/interceptorWorker/RemoteHttpInterceptorWorker.ts +34 -13
- package/src/http/interceptorWorker/types/http.ts +6 -1
- package/src/http/interceptorWorker/types/msw.ts +0 -9
- package/src/http/requestHandler/HttpRequestHandlerClient.ts +2 -4
- package/src/http/requestHandler/errors/TimesCheckError.ts +1 -1
- package/src/http/requestHandler/types/requests.ts +16 -2
- package/src/server/InterceptorServer.ts +13 -5
- package/src/server/constants.ts +1 -1
- package/src/server/errors/UnsupportedResponseBypassError.ts +11 -0
- package/src/utils/crypto.ts +1 -1
- package/src/utils/fetch.ts +25 -6
- package/src/utils/files.ts +1 -1
- package/src/utils/logging.ts +2 -2
- package/src/webSocket/WebSocketClient.ts +1 -1
- package/dist/chunk-6GEP6R3L.mjs.map +0 -1
- package/dist/chunk-PB4TJVK3.js.map +0 -1
package/dist/server.js
CHANGED
|
@@ -1,29 +1,29 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkF5OGZSHS_js = require('./chunk-F5OGZSHS.js');
|
|
4
4
|
require('./chunk-DGUM43GV.js');
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
Object.defineProperty(exports, "DEFAULT_ACCESS_CONTROL_HEADERS", {
|
|
9
9
|
enumerable: true,
|
|
10
|
-
get: function () { return
|
|
10
|
+
get: function () { return chunkF5OGZSHS_js.DEFAULT_ACCESS_CONTROL_HEADERS; }
|
|
11
11
|
});
|
|
12
12
|
Object.defineProperty(exports, "DEFAULT_PREFLIGHT_STATUS_CODE", {
|
|
13
13
|
enumerable: true,
|
|
14
|
-
get: function () { return
|
|
14
|
+
get: function () { return chunkF5OGZSHS_js.DEFAULT_PREFLIGHT_STATUS_CODE; }
|
|
15
15
|
});
|
|
16
16
|
Object.defineProperty(exports, "NotRunningInterceptorServerError", {
|
|
17
17
|
enumerable: true,
|
|
18
|
-
get: function () { return
|
|
18
|
+
get: function () { return chunkF5OGZSHS_js.NotRunningInterceptorServerError_default; }
|
|
19
19
|
});
|
|
20
20
|
Object.defineProperty(exports, "RunningInterceptorServerError", {
|
|
21
21
|
enumerable: true,
|
|
22
|
-
get: function () { return
|
|
22
|
+
get: function () { return chunkF5OGZSHS_js.RunningInterceptorServerError_default; }
|
|
23
23
|
});
|
|
24
24
|
Object.defineProperty(exports, "createInterceptorServer", {
|
|
25
25
|
enumerable: true,
|
|
26
|
-
get: function () { return
|
|
26
|
+
get: function () { return chunkF5OGZSHS_js.createInterceptorServer; }
|
|
27
27
|
});
|
|
28
28
|
//# sourceMappingURL=server.js.map
|
|
29
29
|
//# sourceMappingURL=server.js.map
|
package/dist/server.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { DEFAULT_ACCESS_CONTROL_HEADERS, DEFAULT_PREFLIGHT_STATUS_CODE, NotRunningInterceptorServerError_default as NotRunningInterceptorServerError, RunningInterceptorServerError_default as RunningInterceptorServerError, createInterceptorServer } from './chunk-
|
|
1
|
+
export { DEFAULT_ACCESS_CONTROL_HEADERS, DEFAULT_PREFLIGHT_STATUS_CODE, NotRunningInterceptorServerError_default as NotRunningInterceptorServerError, RunningInterceptorServerError_default as RunningInterceptorServerError, createInterceptorServer } from './chunk-4L2JH2L4.mjs';
|
|
2
2
|
import './chunk-BJTO5JO5.mjs';
|
|
3
3
|
//# sourceMappingURL=server.mjs.map
|
|
4
4
|
//# sourceMappingURL=server.mjs.map
|
package/package.json
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"api",
|
|
15
15
|
"static"
|
|
16
16
|
],
|
|
17
|
-
"version": "1.
|
|
17
|
+
"version": "1.3.0-canary.1",
|
|
18
18
|
"homepage": "https://zimic.dev/docs/interceptor",
|
|
19
19
|
"repository": {
|
|
20
20
|
"type": "git",
|
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
"style:check": "pnpm style --check",
|
|
91
91
|
"style:format": "pnpm style --write",
|
|
92
92
|
"test": "dotenv -v NODE_ENV=test -v FORCE_COLOR=1 -- vitest",
|
|
93
|
-
"test:turbo": "dotenv -v CI=true -- pnpm run test
|
|
93
|
+
"test:turbo": "dotenv -v CI=true -- pnpm run test --coverage --reporter tree",
|
|
94
94
|
"types:check": "tsc --noEmit",
|
|
95
95
|
"postinstall": "node --enable-source-maps -e \"try{require('./dist/scripts/postinstall')}catch(error){console.error(error)}\""
|
|
96
96
|
},
|
package/src/cli/server/start.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
ProcessExitEvent,
|
|
3
|
+
PROCESS_EXIT_EVENTS,
|
|
4
|
+
PROCESS_EXIT_CODE_BY_EXIT_EVENT,
|
|
5
|
+
runCommand,
|
|
6
|
+
CommandError,
|
|
7
|
+
} from '@zimic/utils/process';
|
|
3
8
|
import color from 'picocolors';
|
|
4
9
|
|
|
5
10
|
import { InterceptorServer, createInterceptorServer } from '@/server';
|
|
@@ -8,9 +8,7 @@ import {
|
|
|
8
8
|
HttpSchema,
|
|
9
9
|
} from '@zimic/http';
|
|
10
10
|
import { Default, PossiblePromise } from '@zimic/utils/types';
|
|
11
|
-
import createRegexFromPath from '@zimic/utils/url
|
|
12
|
-
import excludeNonPathParams from '@zimic/utils/url/excludeNonPathParams';
|
|
13
|
-
import validateURLProtocol from '@zimic/utils/url/validateURLProtocol';
|
|
11
|
+
import { createRegexFromPath, excludeNonPathParams, validateURLProtocol } from '@zimic/utils/url';
|
|
14
12
|
|
|
15
13
|
import { isServerSide } from '@/utils/environment';
|
|
16
14
|
|
|
@@ -267,7 +265,7 @@ class HttpInterceptorClient<
|
|
|
267
265
|
}
|
|
268
266
|
}
|
|
269
267
|
|
|
270
|
-
|
|
268
|
+
async handleInterceptedRequest<
|
|
271
269
|
Method extends HttpSchemaMethod<Schema>,
|
|
272
270
|
Path extends HttpSchemaPath<Schema, Method>,
|
|
273
271
|
Context extends HttpInterceptorRequestContext<Schema, Method, Path>,
|
|
@@ -289,15 +287,15 @@ class HttpInterceptorClient<
|
|
|
289
287
|
return null;
|
|
290
288
|
}
|
|
291
289
|
|
|
292
|
-
const response =
|
|
290
|
+
const response = await this.workerOrThrow.createResponseFromDeclaration(request, responseDeclaration);
|
|
293
291
|
|
|
294
|
-
|
|
292
|
+
const shouldSaveInterceptedRequest =
|
|
293
|
+
this.requestSaving.enabled && response && !HttpInterceptorWorker.isRejectedResponse(response);
|
|
294
|
+
|
|
295
|
+
if (shouldSaveInterceptedRequest) {
|
|
295
296
|
const responseClone = response.clone();
|
|
296
297
|
|
|
297
|
-
const parsedResponse = await HttpInterceptorWorker.parseRawResponse<
|
|
298
|
-
Default<Schema[Path][Method]>,
|
|
299
|
-
typeof responseDeclaration.status
|
|
300
|
-
>(responseClone);
|
|
298
|
+
const parsedResponse = await HttpInterceptorWorker.parseRawResponse<Default<Schema[Path][Method]>>(responseClone);
|
|
301
299
|
|
|
302
300
|
matchedHandler.saveInterceptedRequest(parsedRequest, parsedResponse);
|
|
303
301
|
}
|
|
@@ -12,7 +12,13 @@ export type HttpInterceptorPlatform = 'node' | 'browser';
|
|
|
12
12
|
/** @see {@link https://zimic.dev/docs/interceptor/guides/http/unhandled-requests Unhandled requests} */
|
|
13
13
|
export namespace UnhandledRequestStrategy {
|
|
14
14
|
/** @see {@link https://zimic.dev/docs/interceptor/guides/http/unhandled-requests Unhandled requests} */
|
|
15
|
-
export type
|
|
15
|
+
export type LocalAction = 'bypass' | 'reject';
|
|
16
|
+
|
|
17
|
+
/** @see {@link https://zimic.dev/docs/interceptor/guides/http/unhandled-requests Unhandled requests} */
|
|
18
|
+
export type RemoteAction = 'reject';
|
|
19
|
+
|
|
20
|
+
/** @see {@link https://zimic.dev/docs/interceptor/guides/http/unhandled-requests Unhandled requests} */
|
|
21
|
+
export type Action = LocalAction | RemoteAction;
|
|
16
22
|
|
|
17
23
|
/** @see {@link https://zimic.dev/docs/interceptor/guides/http/unhandled-requests Unhandled requests} */
|
|
18
24
|
export interface Declaration<DeclarationAction extends Action = Action> {
|
|
@@ -29,16 +35,16 @@ export namespace UnhandledRequestStrategy {
|
|
|
29
35
|
) => PossiblePromise<Declaration<DeclarationAction>>;
|
|
30
36
|
|
|
31
37
|
/** @see {@link https://zimic.dev/docs/interceptor/guides/http/unhandled-requests Unhandled requests} */
|
|
32
|
-
export type LocalDeclaration = Declaration
|
|
38
|
+
export type LocalDeclaration = Declaration<LocalAction>;
|
|
33
39
|
|
|
34
40
|
/** @see {@link https://zimic.dev/docs/interceptor/guides/http/unhandled-requests Unhandled requests} */
|
|
35
|
-
export type LocalDeclarationFactory = DeclarationFactory
|
|
41
|
+
export type LocalDeclarationFactory = DeclarationFactory<LocalAction>;
|
|
36
42
|
|
|
37
43
|
/** @see {@link https://zimic.dev/docs/interceptor/guides/http/unhandled-requests Unhandled requests} */
|
|
38
|
-
export type RemoteDeclaration = Declaration<
|
|
44
|
+
export type RemoteDeclaration = Declaration<RemoteAction>;
|
|
39
45
|
|
|
40
46
|
/** @see {@link https://zimic.dev/docs/interceptor/guides/http/unhandled-requests Unhandled requests} */
|
|
41
|
-
export type RemoteDeclarationFactory = DeclarationFactory<
|
|
47
|
+
export type RemoteDeclarationFactory = DeclarationFactory<RemoteAction>;
|
|
42
48
|
|
|
43
49
|
/** @see {@link https://zimic.dev/docs/interceptor/guides/http/unhandled-requests Unhandled requests} */
|
|
44
50
|
export type Local = LocalDeclaration | LocalDeclarationFactory;
|
|
@@ -10,8 +10,9 @@ import {
|
|
|
10
10
|
InferPathParams,
|
|
11
11
|
parseHttpBody,
|
|
12
12
|
HttpSearchParams,
|
|
13
|
+
HttpRequest,
|
|
13
14
|
} from '@zimic/http';
|
|
14
|
-
import isDefined from '@zimic/utils/data
|
|
15
|
+
import { isDefined } from '@zimic/utils/data';
|
|
15
16
|
import { Default, PossiblePromise } from '@zimic/utils/types';
|
|
16
17
|
import color from 'picocolors';
|
|
17
18
|
|
|
@@ -33,9 +34,11 @@ import {
|
|
|
33
34
|
HttpInterceptorResponse,
|
|
34
35
|
} from '../requestHandler/types/requests';
|
|
35
36
|
import { DEFAULT_UNHANDLED_REQUEST_STRATEGY } from './constants';
|
|
36
|
-
import {
|
|
37
|
+
import { HttpResponseFactory } from './types/http';
|
|
37
38
|
import { HttpInterceptorWorkerType } from './types/options';
|
|
38
39
|
|
|
40
|
+
const RESPONSE_ACTION_SYMBOL = Symbol.for('HttpResponse.action');
|
|
41
|
+
|
|
39
42
|
abstract class HttpInterceptorWorker {
|
|
40
43
|
abstract get type(): HttpInterceptorWorkerType;
|
|
41
44
|
|
|
@@ -100,7 +103,7 @@ abstract class HttpInterceptorWorker {
|
|
|
100
103
|
interceptor: HttpInterceptorClient<Schema>,
|
|
101
104
|
method: HttpMethod,
|
|
102
105
|
path: string,
|
|
103
|
-
createResponse:
|
|
106
|
+
createResponse: HttpResponseFactory,
|
|
104
107
|
): PossiblePromise<void>;
|
|
105
108
|
|
|
106
109
|
protected async logUnhandledRequestIfNecessary(
|
|
@@ -189,17 +192,71 @@ abstract class HttpInterceptorWorker {
|
|
|
189
192
|
|
|
190
193
|
abstract get interceptorsWithHandlers(): AnyHttpInterceptorClient[];
|
|
191
194
|
|
|
192
|
-
static
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
195
|
+
static setResponseAction(response: Response, action: UnhandledRequestStrategy.Action) {
|
|
196
|
+
Object.defineProperty(response, RESPONSE_ACTION_SYMBOL, {
|
|
197
|
+
value: action,
|
|
198
|
+
enumerable: false,
|
|
199
|
+
configurable: false,
|
|
200
|
+
writable: false,
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
static getResponseAction(response: Response): UnhandledRequestStrategy.Action | undefined {
|
|
205
|
+
if (!(RESPONSE_ACTION_SYMBOL in response)) {
|
|
206
|
+
return undefined;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const action = response[RESPONSE_ACTION_SYMBOL];
|
|
210
|
+
|
|
211
|
+
/* istanbul ignore if -- @preserve
|
|
212
|
+
* This is just a type guard to ensure the value is valid. In practice, this condition should never be true. */
|
|
213
|
+
if (action !== 'bypass' && action !== 'reject') {
|
|
214
|
+
return undefined;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return action;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
private createBypassedResponse() {
|
|
221
|
+
const response = Response.redirect('about:blank', 302) as HttpResponse;
|
|
222
|
+
HttpInterceptorWorker.setResponseAction(response, 'bypass');
|
|
223
|
+
return response;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
static isBypassedResponse(response: Response) {
|
|
227
|
+
return this.getResponseAction(response) === 'bypass';
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
private createRejectedResponse() {
|
|
231
|
+
const response = Response.error() as HttpResponse;
|
|
232
|
+
HttpInterceptorWorker.setResponseAction(response, 'reject');
|
|
233
|
+
return response;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
static isRejectedResponse(response: Response) {
|
|
237
|
+
return this.getResponseAction(response) === 'reject';
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
createResponseFromDeclaration(
|
|
241
|
+
request: HttpRequest,
|
|
242
|
+
declaration:
|
|
243
|
+
| { status: number; headers?: HttpHeadersInit; body?: HttpBody }
|
|
244
|
+
| { action: UnhandledRequestStrategy.Action },
|
|
245
|
+
): PossiblePromise<HttpResponse | null> {
|
|
246
|
+
if ('action' in declaration) {
|
|
247
|
+
if (declaration.action === 'bypass') {
|
|
248
|
+
return this.createBypassedResponse();
|
|
249
|
+
} else {
|
|
250
|
+
return this.createRejectedResponse();
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
196
254
|
const headers = new HttpHeaders(declaration.headers);
|
|
197
|
-
const status = declaration.status;
|
|
198
255
|
|
|
199
|
-
const canHaveBody = methodCanHaveResponseBody(request.method as HttpMethod) && status !== 204;
|
|
256
|
+
const canHaveBody = methodCanHaveResponseBody(request.method as HttpMethod) && declaration.status !== 204;
|
|
200
257
|
|
|
201
258
|
if (!canHaveBody) {
|
|
202
|
-
return new Response(null, { headers, status }) as HttpResponse;
|
|
259
|
+
return new Response(null, { headers, status: declaration.status }) as HttpResponse;
|
|
203
260
|
}
|
|
204
261
|
|
|
205
262
|
if (
|
|
@@ -212,10 +269,10 @@ abstract class HttpInterceptorWorker {
|
|
|
212
269
|
declaration.body instanceof ArrayBuffer ||
|
|
213
270
|
declaration.body instanceof ReadableStream
|
|
214
271
|
) {
|
|
215
|
-
return new Response(declaration.body ?? null, { headers, status }) as HttpResponse;
|
|
272
|
+
return new Response(declaration.body ?? null, { headers, status: declaration.status }) as HttpResponse;
|
|
216
273
|
}
|
|
217
274
|
|
|
218
|
-
return Response.json(declaration.body, { headers, status }) as HttpResponse;
|
|
275
|
+
return Response.json(declaration.body, { headers, status: declaration.status }) as HttpResponse;
|
|
219
276
|
}
|
|
220
277
|
|
|
221
278
|
static async parseRawUnhandledRequest(request: Request) {
|
|
@@ -305,9 +362,10 @@ abstract class HttpInterceptorWorker {
|
|
|
305
362
|
return HTTP_INTERCEPTOR_REQUEST_HIDDEN_PROPERTIES.has(property as never);
|
|
306
363
|
}
|
|
307
364
|
|
|
308
|
-
static async parseRawResponse<
|
|
309
|
-
|
|
310
|
-
|
|
365
|
+
static async parseRawResponse<
|
|
366
|
+
MethodSchema extends HttpMethodSchema,
|
|
367
|
+
StatusCode extends HttpStatusCode = HttpStatusCode,
|
|
368
|
+
>(originalRawResponse: Response): Promise<HttpInterceptorResponse<MethodSchema, StatusCode>> {
|
|
311
369
|
const rawResponse = originalRawResponse.clone();
|
|
312
370
|
const rawResponseClone = rawResponse.clone();
|
|
313
371
|
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import { HttpRequest, HttpResponse, HttpMethod, HttpSchema } from '@zimic/http';
|
|
2
|
-
import createRegexFromPath from '@zimic/utils/url
|
|
3
|
-
import
|
|
4
|
-
import validatePathParams from '@zimic/utils/url/validatePathParams';
|
|
5
|
-
import { SharedOptions as MSWWorkerSharedOptions, http, passthrough } from 'msw';
|
|
1
|
+
import { HttpRequest, HttpResponse, HttpMethod, HttpSchema, HttpHeadersInit, HttpBody } from '@zimic/http';
|
|
2
|
+
import { createRegexFromPath, excludeNonPathParams, validatePathParams } from '@zimic/utils/url';
|
|
3
|
+
import { SharedOptions as MSWWorkerSharedOptions, bypass, http, passthrough } from 'msw';
|
|
6
4
|
import * as mswBrowser from 'msw/browser';
|
|
7
5
|
import * as mswNode from 'msw/node';
|
|
8
6
|
|
|
@@ -12,10 +10,11 @@ import { isClientSide, isServerSide } from '@/utils/environment';
|
|
|
12
10
|
import NotRunningHttpInterceptorError from '../interceptor/errors/NotRunningHttpInterceptorError';
|
|
13
11
|
import UnknownHttpInterceptorPlatformError from '../interceptor/errors/UnknownHttpInterceptorPlatformError';
|
|
14
12
|
import HttpInterceptorClient, { AnyHttpInterceptorClient } from '../interceptor/HttpInterceptorClient';
|
|
13
|
+
import { UnhandledRequestStrategy } from '../interceptor/types/options';
|
|
15
14
|
import UnregisteredBrowserServiceWorkerError from './errors/UnregisteredBrowserServiceWorkerError';
|
|
16
15
|
import HttpInterceptorWorker from './HttpInterceptorWorker';
|
|
17
|
-
import { HttpResponseFactoryContext } from './types/http';
|
|
18
|
-
import { BrowserMSWWorker,
|
|
16
|
+
import { HttpResponseFactory, HttpResponseFactoryContext } from './types/http';
|
|
17
|
+
import { BrowserMSWWorker, MSWWorker, NodeMSWWorker } from './types/msw';
|
|
19
18
|
import { LocalHttpInterceptorWorkerOptions } from './types/options';
|
|
20
19
|
|
|
21
20
|
interface HttpHandler {
|
|
@@ -163,7 +162,7 @@ class LocalHttpInterceptorWorker extends HttpInterceptorWorker {
|
|
|
163
162
|
interceptor: HttpInterceptorClient<Schema>,
|
|
164
163
|
method: HttpMethod,
|
|
165
164
|
path: string,
|
|
166
|
-
createResponse:
|
|
165
|
+
createResponse: HttpResponseFactory,
|
|
167
166
|
) {
|
|
168
167
|
if (!this.isRunning) {
|
|
169
168
|
throw new NotRunningHttpInterceptorError();
|
|
@@ -185,7 +184,7 @@ class LocalHttpInterceptorWorker extends HttpInterceptorWorker {
|
|
|
185
184
|
let response: HttpResponse | null = null;
|
|
186
185
|
|
|
187
186
|
try {
|
|
188
|
-
response = await createResponse({
|
|
187
|
+
response = await createResponse({ request });
|
|
189
188
|
} catch (error) {
|
|
190
189
|
console.error(error);
|
|
191
190
|
}
|
|
@@ -209,6 +208,32 @@ class LocalHttpInterceptorWorker extends HttpInterceptorWorker {
|
|
|
209
208
|
methodHandlers.push(handler);
|
|
210
209
|
}
|
|
211
210
|
|
|
211
|
+
async createResponseFromDeclaration(
|
|
212
|
+
request: HttpRequest,
|
|
213
|
+
declaration:
|
|
214
|
+
| { status: number; headers?: HttpHeadersInit; body?: HttpBody }
|
|
215
|
+
| { action: UnhandledRequestStrategy.Action },
|
|
216
|
+
) {
|
|
217
|
+
const requestClone = request.clone();
|
|
218
|
+
const response = await super.createResponseFromDeclaration(request, declaration);
|
|
219
|
+
|
|
220
|
+
if (response && HttpInterceptorWorker.isBypassedResponse(response)) {
|
|
221
|
+
try {
|
|
222
|
+
const response = (await fetch(bypass(requestClone))) as HttpResponse;
|
|
223
|
+
return response;
|
|
224
|
+
} catch (error) {
|
|
225
|
+
console.error(error);
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (response && HttpInterceptorWorker.isRejectedResponse(response)) {
|
|
231
|
+
return response;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return response;
|
|
235
|
+
}
|
|
236
|
+
|
|
212
237
|
private async createResponseForRequest(request: HttpRequest) {
|
|
213
238
|
const methodHandlers = this.httpHandlersByMethod[request.method as HttpMethod];
|
|
214
239
|
|
|
@@ -244,9 +269,9 @@ class LocalHttpInterceptorWorker extends HttpInterceptorWorker {
|
|
|
244
269
|
await super.logUnhandledRequestIfNecessary(requestClone, strategy);
|
|
245
270
|
|
|
246
271
|
if (strategy?.action === 'reject') {
|
|
247
|
-
return Response.error();
|
|
272
|
+
return Response.error() as HttpResponse;
|
|
248
273
|
} else {
|
|
249
|
-
return passthrough();
|
|
274
|
+
return passthrough() as HttpResponse;
|
|
250
275
|
}
|
|
251
276
|
}
|
|
252
277
|
|
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
import { HttpMethod, HttpSchema } from '@zimic/http';
|
|
2
|
-
import
|
|
1
|
+
import { HttpBody, HttpHeadersInit, HttpMethod, HttpRequest, HttpSchema } from '@zimic/http';
|
|
2
|
+
import { PossiblePromise } from '@zimic/utils/types';
|
|
3
|
+
import { validatePathParams } from '@zimic/utils/url';
|
|
3
4
|
|
|
5
|
+
import UnsupportedResponseBypassError from '@/server/errors/UnsupportedResponseBypassError';
|
|
4
6
|
import { HttpHandlerCommit, InterceptorServerWebSocketSchema } from '@/server/types/schema';
|
|
5
7
|
import { importCrypto } from '@/utils/crypto';
|
|
6
8
|
import { isClientSide, isServerSide } from '@/utils/environment';
|
|
7
9
|
import { deserializeRequest, serializeResponse } from '@/utils/fetch';
|
|
10
|
+
import { methodCanHaveResponseBody } from '@/utils/http';
|
|
8
11
|
import { WebSocketMessageAbortError } from '@/utils/webSocket';
|
|
9
12
|
import { WebSocketEventMessage } from '@/webSocket/types';
|
|
10
13
|
import WebSocketClient from '@/webSocket/WebSocketClient';
|
|
@@ -12,10 +15,9 @@ import WebSocketClient from '@/webSocket/WebSocketClient';
|
|
|
12
15
|
import NotRunningHttpInterceptorError from '../interceptor/errors/NotRunningHttpInterceptorError';
|
|
13
16
|
import UnknownHttpInterceptorPlatformError from '../interceptor/errors/UnknownHttpInterceptorPlatformError';
|
|
14
17
|
import HttpInterceptorClient, { AnyHttpInterceptorClient } from '../interceptor/HttpInterceptorClient';
|
|
15
|
-
import { HttpInterceptorPlatform } from '../interceptor/types/options';
|
|
18
|
+
import { HttpInterceptorPlatform, UnhandledRequestStrategy } from '../interceptor/types/options';
|
|
16
19
|
import HttpInterceptorWorker from './HttpInterceptorWorker';
|
|
17
|
-
import { HttpResponseFactoryContext } from './types/http';
|
|
18
|
-
import { MSWHttpResponseFactory } from './types/msw';
|
|
20
|
+
import { HttpResponseFactory, HttpResponseFactoryContext } from './types/http';
|
|
19
21
|
import { RemoteHttpInterceptorWorkerOptions } from './types/options';
|
|
20
22
|
|
|
21
23
|
interface HttpHandler {
|
|
@@ -24,7 +26,7 @@ interface HttpHandler {
|
|
|
24
26
|
method: HttpMethod;
|
|
25
27
|
path: string;
|
|
26
28
|
interceptor: AnyHttpInterceptorClient;
|
|
27
|
-
createResponse: (context: HttpResponseFactoryContext) =>
|
|
29
|
+
createResponse: (context: HttpResponseFactoryContext) => PossiblePromise<Response | null>;
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
class RemoteHttpInterceptorWorker extends HttpInterceptorWorker {
|
|
@@ -78,9 +80,12 @@ class RemoteHttpInterceptorWorker extends HttpInterceptorWorker {
|
|
|
78
80
|
|
|
79
81
|
try {
|
|
80
82
|
const rawResponse = (await handler?.createResponse({ request })) ?? null;
|
|
81
|
-
const response = rawResponse && request.method === 'HEAD' ? new Response(null, rawResponse) : rawResponse;
|
|
82
83
|
|
|
83
|
-
if (
|
|
84
|
+
if (rawResponse) {
|
|
85
|
+
const response = methodCanHaveResponseBody(request.method as HttpMethod)
|
|
86
|
+
? rawResponse
|
|
87
|
+
: new Response(null, rawResponse);
|
|
88
|
+
|
|
84
89
|
return { response: await serializeResponse(response) };
|
|
85
90
|
}
|
|
86
91
|
} catch (error) {
|
|
@@ -135,7 +140,7 @@ class RemoteHttpInterceptorWorker extends HttpInterceptorWorker {
|
|
|
135
140
|
interceptor: HttpInterceptorClient<Schema>,
|
|
136
141
|
method: HttpMethod,
|
|
137
142
|
path: string,
|
|
138
|
-
createResponse:
|
|
143
|
+
createResponse: HttpResponseFactory,
|
|
139
144
|
) {
|
|
140
145
|
if (!this.isRunning) {
|
|
141
146
|
throw new NotRunningHttpInterceptorError();
|
|
@@ -151,10 +156,7 @@ class RemoteHttpInterceptorWorker extends HttpInterceptorWorker {
|
|
|
151
156
|
method,
|
|
152
157
|
path,
|
|
153
158
|
interceptor,
|
|
154
|
-
|
|
155
|
-
const response = await createResponse(context);
|
|
156
|
-
return response;
|
|
157
|
-
},
|
|
159
|
+
createResponse,
|
|
158
160
|
};
|
|
159
161
|
|
|
160
162
|
this.httpHandlers.set(handler.id, handler);
|
|
@@ -167,6 +169,25 @@ class RemoteHttpInterceptorWorker extends HttpInterceptorWorker {
|
|
|
167
169
|
});
|
|
168
170
|
}
|
|
169
171
|
|
|
172
|
+
async createResponseFromDeclaration(
|
|
173
|
+
request: HttpRequest,
|
|
174
|
+
declaration:
|
|
175
|
+
| { status: number; headers?: HttpHeadersInit; body?: HttpBody }
|
|
176
|
+
| { action: UnhandledRequestStrategy.Action },
|
|
177
|
+
) {
|
|
178
|
+
const response = await super.createResponseFromDeclaration(request, declaration);
|
|
179
|
+
|
|
180
|
+
if (response && HttpInterceptorWorker.isBypassedResponse(response)) {
|
|
181
|
+
throw new UnsupportedResponseBypassError();
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (response && HttpInterceptorWorker.isRejectedResponse(response)) {
|
|
185
|
+
return response;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return response;
|
|
189
|
+
}
|
|
190
|
+
|
|
170
191
|
async clearHandlers<Schema extends HttpSchema>(
|
|
171
192
|
options: {
|
|
172
193
|
interceptor?: HttpInterceptorClient<Schema>;
|
|
@@ -1,5 +1,10 @@
|
|
|
1
|
-
import { HttpBody, HttpRequest } from '@zimic/http';
|
|
1
|
+
import { HttpBody, HttpRequest, HttpResponse } from '@zimic/http';
|
|
2
|
+
import { PossiblePromise } from '@zimic/utils/types';
|
|
2
3
|
|
|
3
4
|
export interface HttpResponseFactoryContext<Body extends HttpBody = HttpBody> {
|
|
4
5
|
request: HttpRequest<Body>;
|
|
5
6
|
}
|
|
7
|
+
|
|
8
|
+
export type HttpResponseFactory<RequestBody extends HttpBody = HttpBody, ResponseBody extends HttpBody = HttpBody> = (
|
|
9
|
+
context: HttpResponseFactoryContext<RequestBody>,
|
|
10
|
+
) => PossiblePromise<HttpResponse<ResponseBody> | null>;
|
|
@@ -1,16 +1,7 @@
|
|
|
1
|
-
import { HttpResponse, HttpBody } from '@zimic/http';
|
|
2
|
-
import { PossiblePromise } from '@zimic/utils/types';
|
|
3
1
|
import type { HttpHandler as MSWHandler } from 'msw';
|
|
4
2
|
import type { SetupWorker as BrowserMSWWorker } from 'msw/browser';
|
|
5
3
|
import type { SetupServer as NodeMSWWorker } from 'msw/node';
|
|
6
4
|
|
|
7
|
-
import { HttpResponseFactoryContext } from './http';
|
|
8
|
-
|
|
9
5
|
export type { MSWHandler, NodeMSWWorker, BrowserMSWWorker };
|
|
10
6
|
|
|
11
7
|
export type MSWWorker = NodeMSWWorker | BrowserMSWWorker;
|
|
12
|
-
|
|
13
|
-
export type MSWHttpResponseFactory<
|
|
14
|
-
RequestBody extends HttpBody = HttpBody,
|
|
15
|
-
ResponseBody extends HttpBody = HttpBody,
|
|
16
|
-
> = (context: HttpResponseFactoryContext<RequestBody>) => PossiblePromise<HttpResponse<ResponseBody> | null>;
|
|
@@ -9,10 +9,8 @@ import {
|
|
|
9
9
|
HttpRequestSearchParamsSchema,
|
|
10
10
|
HttpRequestHeadersSchema,
|
|
11
11
|
} from '@zimic/http';
|
|
12
|
-
import blobEquals from '@zimic/utils/data
|
|
13
|
-
import
|
|
14
|
-
import jsonEquals from '@zimic/utils/data/jsonEquals';
|
|
15
|
-
import waitForDelay from '@zimic/utils/time/waitForDelay';
|
|
12
|
+
import { blobEquals, jsonContains, jsonEquals } from '@zimic/utils/data';
|
|
13
|
+
import { waitForDelay } from '@zimic/utils/time';
|
|
16
14
|
import { Default, Range } from '@zimic/utils/types';
|
|
17
15
|
|
|
18
16
|
import { convertArrayBufferToBlob, convertReadableStreamToBlob } from '@/utils/data';
|
|
@@ -16,6 +16,8 @@ import {
|
|
|
16
16
|
} from '@zimic/http';
|
|
17
17
|
import { Default, PartialByKey, PossiblePromise, Replace } from '@zimic/utils/types';
|
|
18
18
|
|
|
19
|
+
import { UnhandledRequestStrategy } from '@/http/interceptor/types/options';
|
|
20
|
+
|
|
19
21
|
type HttpRequestHandlerResponseBody<
|
|
20
22
|
ResponseSchema extends HttpResponseSchema,
|
|
21
23
|
StatusCode extends HttpStatusCode,
|
|
@@ -42,17 +44,29 @@ export type HttpRequestHandlerResponseDeclarationWithHeaders<ResponseSchema exte
|
|
|
42
44
|
: { headers: HttpRequestHandlerResponseDeclarationHeaders<ResponseSchema> };
|
|
43
45
|
|
|
44
46
|
/** @see {@link https://zimic.dev/docs/interceptor/api/http-request-handler#handlerrespond `handler.respond()` API reference} */
|
|
45
|
-
export type
|
|
47
|
+
export type HttpRequestHandlerStatusResponseDeclaration<
|
|
46
48
|
MethodSchema extends HttpMethodSchema = HttpMethodSchema,
|
|
47
49
|
StatusCode extends HttpStatusCode = HttpStatusCode,
|
|
48
50
|
> = StatusCode extends StatusCode
|
|
49
|
-
? { status: StatusCode } & HttpRequestHandlerResponseWithBody<
|
|
51
|
+
? { status: StatusCode; action?: never } & HttpRequestHandlerResponseWithBody<
|
|
50
52
|
Default<Default<MethodSchema['response']>[StatusCode]>,
|
|
51
53
|
StatusCode
|
|
52
54
|
> &
|
|
53
55
|
HttpRequestHandlerResponseDeclarationWithHeaders<Default<Default<MethodSchema['response']>[StatusCode]>>
|
|
54
56
|
: never;
|
|
55
57
|
|
|
58
|
+
/** @see {@link https://zimic.dev/docs/interceptor/api/http-request-handler#handlerrespond `handler.respond()` API reference} */
|
|
59
|
+
export interface HttpRequestHandlerActionResponseDeclaration {
|
|
60
|
+
status?: never;
|
|
61
|
+
action: UnhandledRequestStrategy.Action;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** @see {@link https://zimic.dev/docs/interceptor/api/http-request-handler#handlerrespond `handler.respond()` API reference} */
|
|
65
|
+
export type HttpRequestHandlerResponseDeclaration<
|
|
66
|
+
MethodSchema extends HttpMethodSchema = HttpMethodSchema,
|
|
67
|
+
StatusCode extends HttpStatusCode = HttpStatusCode,
|
|
68
|
+
> = HttpRequestHandlerStatusResponseDeclaration<MethodSchema, StatusCode> | HttpRequestHandlerActionResponseDeclaration;
|
|
69
|
+
|
|
56
70
|
/** @see {@link https://zimic.dev/docs/interceptor/api/http-request-handler#handlerrespond `handler.respond()` API reference} */
|
|
57
71
|
export type HttpRequestHandlerResponseDeclarationFactory<
|
|
58
72
|
Path extends string,
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { normalizeNodeRequest, sendNodeResponse } from '@whatwg-node/server';
|
|
2
2
|
import { HttpRequest, HttpMethod } from '@zimic/http';
|
|
3
|
-
import { startHttpServer, stopHttpServer, getHttpServerPort } from '@zimic/utils/server
|
|
4
|
-
import createRegexFromPath from '@zimic/utils/url
|
|
5
|
-
import excludeNonPathParams from '@zimic/utils/url/excludeNonPathParams';
|
|
3
|
+
import { startHttpServer, stopHttpServer, getHttpServerPort } from '@zimic/utils/server';
|
|
4
|
+
import { createRegexFromPath, excludeNonPathParams } from '@zimic/utils/url';
|
|
6
5
|
import { createServer, Server as HttpServer, IncomingMessage, ServerResponse } from 'http';
|
|
7
6
|
import type { WebSocket as Socket } from 'isomorphic-ws';
|
|
8
7
|
|
|
@@ -297,8 +296,17 @@ class InterceptorServer implements PublicInterceptorServer {
|
|
|
297
296
|
const { response, matchedSomeInterceptor } = await this.createResponseForRequest(serializedRequest);
|
|
298
297
|
|
|
299
298
|
if (response) {
|
|
300
|
-
|
|
301
|
-
|
|
299
|
+
if (HttpInterceptorWorker.isRejectedResponse(response)) {
|
|
300
|
+
nodeResponse.destroy();
|
|
301
|
+
} else {
|
|
302
|
+
this.setDefaultAccessControlHeaders(response, [
|
|
303
|
+
'access-control-allow-origin',
|
|
304
|
+
'access-control-expose-headers',
|
|
305
|
+
]);
|
|
306
|
+
|
|
307
|
+
await sendNodeResponse(response, nodeResponse, nodeRequest, true);
|
|
308
|
+
}
|
|
309
|
+
|
|
302
310
|
return;
|
|
303
311
|
}
|
|
304
312
|
|
package/src/server/constants.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { HTTP_METHODS, HttpSchema } from '@zimic/http';
|
|
2
|
-
import { DEFAULT_HTTP_SERVER_LIFECYCLE_TIMEOUT } from '@zimic/utils/server
|
|
2
|
+
import { DEFAULT_HTTP_SERVER_LIFECYCLE_TIMEOUT } from '@zimic/utils/server';
|
|
3
3
|
|
|
4
4
|
import { DEFAULT_WEB_SOCKET_MESSAGE_TIMEOUT } from '@/utils/webSocket';
|
|
5
5
|
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
class UnsupportedResponseBypassError extends Error {
|
|
2
|
+
constructor() {
|
|
3
|
+
super(
|
|
4
|
+
"Remote interceptors cannot bypass responses. Use `{ action: 'reject' }` instead.\n\n" +
|
|
5
|
+
'Learn more: https://zimic.dev/docs/interceptor/api/http-request-handler#handlerrespond',
|
|
6
|
+
);
|
|
7
|
+
this.name = 'UnsupportedResponseBypassError';
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export default UnsupportedResponseBypassError;
|
package/src/utils/crypto.ts
CHANGED