@zimic/interceptor 1.1.0-canary.0 → 1.1.0-canary.2
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-LWUFSWRA.js → chunk-5XPJX7EC.js} +108 -37
- package/dist/chunk-5XPJX7EC.js.map +1 -0
- package/dist/{chunk-7DRPXOOJ.mjs → chunk-OQONGS2R.mjs} +108 -37
- package/dist/chunk-OQONGS2R.mjs.map +1 -0
- package/dist/cli.js +18 -18
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +2 -2
- package/dist/cli.mjs.map +1 -1
- package/dist/http.js +199 -104
- package/dist/http.js.map +1 -1
- package/dist/http.mjs +199 -104
- package/dist/http.mjs.map +1 -1
- package/dist/server.js +6 -6
- package/dist/server.mjs +1 -1
- package/package.json +5 -4
- package/src/http/interceptor/HttpInterceptorClient.ts +7 -8
- package/src/http/interceptor/types/requests.ts +3 -2
- package/src/http/interceptorWorker/HttpInterceptorWorker.ts +9 -7
- package/src/http/interceptorWorker/LocalHttpInterceptorWorker.ts +113 -50
- package/src/http/interceptorWorker/RemoteHttpInterceptorWorker.ts +17 -18
- package/src/http/interceptorWorker/types/http.ts +5 -0
- package/src/http/interceptorWorker/types/msw.ts +4 -6
- package/src/server/InterceptorServer.ts +25 -27
- package/src/server/types/schema.ts +2 -1
- package/dist/chunk-7DRPXOOJ.mjs.map +0 -1
- package/dist/chunk-LWUFSWRA.js.map +0 -1
package/dist/server.js
CHANGED
|
@@ -1,29 +1,29 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunk5XPJX7EC_js = require('./chunk-5XPJX7EC.js');
|
|
4
4
|
require('./chunk-WCQVDF3K.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 chunk5XPJX7EC_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 chunk5XPJX7EC_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 chunk5XPJX7EC_js.NotRunningInterceptorServerError_default; }
|
|
19
19
|
});
|
|
20
20
|
Object.defineProperty(exports, "RunningInterceptorServerError", {
|
|
21
21
|
enumerable: true,
|
|
22
|
-
get: function () { return
|
|
22
|
+
get: function () { return chunk5XPJX7EC_js.RunningInterceptorServerError_default; }
|
|
23
23
|
});
|
|
24
24
|
Object.defineProperty(exports, "createInterceptorServer", {
|
|
25
25
|
enumerable: true,
|
|
26
|
-
get: function () { return
|
|
26
|
+
get: function () { return chunk5XPJX7EC_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-OQONGS2R.mjs';
|
|
2
2
|
import './chunk-CGILA3WO.mjs';
|
|
3
3
|
//# sourceMappingURL=server.mjs.map
|
|
4
4
|
//# sourceMappingURL=server.mjs.map
|
package/package.json
CHANGED
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
"api",
|
|
15
15
|
"static"
|
|
16
16
|
],
|
|
17
|
-
"version": "1.1.0-canary.
|
|
17
|
+
"version": "1.1.0-canary.2",
|
|
18
|
+
"homepage": "https://zimic.dev/docs/interceptor",
|
|
18
19
|
"repository": {
|
|
19
20
|
"type": "git",
|
|
20
21
|
"url": "https://github.com/zimicjs/zimic.git",
|
|
@@ -104,13 +105,13 @@
|
|
|
104
105
|
"typescript": "^5.9.2",
|
|
105
106
|
"vitest": "^3.2.4",
|
|
106
107
|
"@zimic/eslint-config-node": "0.0.0",
|
|
107
|
-
"@zimic/tsconfig": "0.0.0",
|
|
108
108
|
"@zimic/lint-staged-config": "0.0.0",
|
|
109
|
-
"@zimic/utils": "0.0.0"
|
|
109
|
+
"@zimic/utils": "0.0.0",
|
|
110
|
+
"@zimic/tsconfig": "0.0.0"
|
|
110
111
|
},
|
|
111
112
|
"peerDependencies": {
|
|
112
113
|
"typescript": ">=5.0.0",
|
|
113
|
-
"@zimic/http": "^1.0.0 || 1.1.0-canary.
|
|
114
|
+
"@zimic/http": "^1.0.0 || 1.1.0-canary.2"
|
|
114
115
|
},
|
|
115
116
|
"peerDependenciesMeta": {
|
|
116
117
|
"typescript": {
|
|
@@ -8,9 +8,8 @@ import {
|
|
|
8
8
|
HttpSchema,
|
|
9
9
|
} from '@zimic/http';
|
|
10
10
|
import { Default, PossiblePromise } from '@zimic/utils/types';
|
|
11
|
-
import
|
|
11
|
+
import createParametrizedPathPattern from '@zimic/utils/url/createParametrizedPathPattern';
|
|
12
12
|
import excludeURLParams from '@zimic/utils/url/excludeURLParams';
|
|
13
|
-
import joinURL from '@zimic/utils/url/joinURL';
|
|
14
13
|
import validateURLProtocol from '@zimic/utils/url/validateURLProtocol';
|
|
15
14
|
|
|
16
15
|
import { isServerSide } from '@/utils/environment';
|
|
@@ -246,14 +245,13 @@ class HttpInterceptorClient<
|
|
|
246
245
|
|
|
247
246
|
this.handlerClientsByMethod[handler.method].set(handler.path, handlerClients);
|
|
248
247
|
|
|
249
|
-
const
|
|
250
|
-
const urlRegex = createRegExpFromURL(url);
|
|
248
|
+
const pathPattern = createParametrizedPathPattern(handler.path);
|
|
251
249
|
|
|
252
|
-
const registrationResult = this.workerOrThrow.use(this, handler.method,
|
|
250
|
+
const registrationResult = this.workerOrThrow.use(this, handler.method, handler.path, async (context) => {
|
|
253
251
|
const response = await this.handleInterceptedRequest(
|
|
254
|
-
urlRegex,
|
|
255
252
|
handler.method,
|
|
256
253
|
handler.path,
|
|
254
|
+
pathPattern,
|
|
257
255
|
context as HttpInterceptorRequestContext<Schema, Method, Path>,
|
|
258
256
|
);
|
|
259
257
|
return response;
|
|
@@ -268,9 +266,10 @@ class HttpInterceptorClient<
|
|
|
268
266
|
Method extends HttpSchemaMethod<Schema>,
|
|
269
267
|
Path extends HttpSchemaPath<Schema, Method>,
|
|
270
268
|
Context extends HttpInterceptorRequestContext<Schema, Method, Path>,
|
|
271
|
-
>(
|
|
269
|
+
>(method: Method, path: Path, pathPattern: RegExp, { request }: Context): Promise<HttpResponse | null> {
|
|
272
270
|
const parsedRequest = await HttpInterceptorWorker.parseRawRequest<Path, Default<Schema[Path][Method]>>(request, {
|
|
273
|
-
|
|
271
|
+
baseURL: this.baseURLAsString,
|
|
272
|
+
pathPattern,
|
|
274
273
|
});
|
|
275
274
|
|
|
276
275
|
const matchedHandler = await this.findMatchedHandler(method, path, parsedRequest);
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { HttpBody, HttpSchema, HttpSchemaMethod, HttpSchemaPath } from '@zimic/http';
|
|
2
2
|
import { Default } from '@zimic/utils/types';
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { HttpResponseFactoryContext } from '@/http/interceptorWorker/types/http';
|
|
5
|
+
|
|
5
6
|
import { HttpInterceptorRequest } from '../../requestHandler/types/requests';
|
|
6
7
|
|
|
7
8
|
export type HttpInterceptorRequestContext<
|
|
8
9
|
Schema extends HttpSchema,
|
|
9
10
|
Method extends HttpSchemaMethod<Schema>,
|
|
10
11
|
Path extends HttpSchemaPath<Schema, Method>,
|
|
11
|
-
> =
|
|
12
|
+
> = HttpResponseFactoryContext<Default<Default<Schema[Path][Method]>['request']>['body']>;
|
|
12
13
|
|
|
13
14
|
export type UnhandledHttpInterceptorRequestPath = string;
|
|
14
15
|
|
|
@@ -101,7 +101,7 @@ abstract class HttpInterceptorWorker {
|
|
|
101
101
|
abstract use<Schema extends HttpSchema>(
|
|
102
102
|
interceptor: HttpInterceptorClient<Schema>,
|
|
103
103
|
method: HttpMethod,
|
|
104
|
-
|
|
104
|
+
path: string,
|
|
105
105
|
createResponse: MSWHttpResponseFactory,
|
|
106
106
|
): PossiblePromise<void>;
|
|
107
107
|
|
|
@@ -229,7 +229,7 @@ abstract class HttpInterceptorWorker {
|
|
|
229
229
|
|
|
230
230
|
static async parseRawRequest<Path extends string, MethodSchema extends HttpMethodSchema>(
|
|
231
231
|
originalRawRequest: Request,
|
|
232
|
-
options
|
|
232
|
+
options?: { baseURL: string; pathPattern: RegExp },
|
|
233
233
|
): Promise<HttpInterceptorRequest<Path, MethodSchema>> {
|
|
234
234
|
const rawRequest = originalRawRequest.clone();
|
|
235
235
|
const rawRequestClone = rawRequest.clone();
|
|
@@ -240,7 +240,7 @@ abstract class HttpInterceptorWorker {
|
|
|
240
240
|
type HeadersSchema = Default<Default<MethodSchema['request']>['headers']>;
|
|
241
241
|
const headers = new HttpHeaders<HeadersSchema>(rawRequest.headers);
|
|
242
242
|
|
|
243
|
-
const pathParams =
|
|
243
|
+
const pathParams = this.parseRawPathParams<Path>(rawRequest, options);
|
|
244
244
|
|
|
245
245
|
const parsedURL = new URL(rawRequest.url);
|
|
246
246
|
type SearchParamsSchema = Default<Default<MethodSchema['request']>['searchParams']>;
|
|
@@ -360,10 +360,12 @@ abstract class HttpInterceptorWorker {
|
|
|
360
360
|
return HTTP_INTERCEPTOR_RESPONSE_HIDDEN_PROPERTIES.has(property as never);
|
|
361
361
|
}
|
|
362
362
|
|
|
363
|
-
static parseRawPathParams<Path extends string>(
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
363
|
+
static parseRawPathParams<Path extends string>(
|
|
364
|
+
request: Request,
|
|
365
|
+
options?: { baseURL: string; pathPattern: RegExp },
|
|
366
|
+
): InferPathParams<Path> {
|
|
367
|
+
const match = options?.pathPattern.exec(request.url.replace(options.baseURL, ''));
|
|
368
|
+
return { ...match?.groups } as InferPathParams<Path>;
|
|
367
369
|
}
|
|
368
370
|
|
|
369
371
|
static async parseRawBody<Body extends HttpBody>(resource: Request | Response) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { HttpRequest, HttpResponse, HttpMethod, HttpSchema } from '@zimic/http';
|
|
2
|
+
import createParametrizedPathPattern from '@zimic/utils/url/createParametrizedPathPattern';
|
|
2
3
|
import excludeURLParams from '@zimic/utils/url/excludeURLParams';
|
|
3
|
-
import
|
|
4
|
+
import validatePathParams from '@zimic/utils/url/validatePathParams';
|
|
4
5
|
import { SharedOptions as MSWWorkerSharedOptions, http, passthrough } from 'msw';
|
|
5
6
|
import * as mswBrowser from 'msw/browser';
|
|
6
7
|
import * as mswNode from 'msw/node';
|
|
@@ -10,29 +11,38 @@ import { isClientSide, isServerSide } from '@/utils/environment';
|
|
|
10
11
|
|
|
11
12
|
import NotRunningHttpInterceptorError from '../interceptor/errors/NotRunningHttpInterceptorError';
|
|
12
13
|
import UnknownHttpInterceptorPlatformError from '../interceptor/errors/UnknownHttpInterceptorPlatformError';
|
|
13
|
-
import HttpInterceptorClient from '../interceptor/HttpInterceptorClient';
|
|
14
|
+
import HttpInterceptorClient, { AnyHttpInterceptorClient } from '../interceptor/HttpInterceptorClient';
|
|
14
15
|
import UnregisteredBrowserServiceWorkerError from './errors/UnregisteredBrowserServiceWorkerError';
|
|
15
16
|
import HttpInterceptorWorker from './HttpInterceptorWorker';
|
|
16
|
-
import {
|
|
17
|
+
import { HttpResponseFactoryContext } from './types/http';
|
|
18
|
+
import { BrowserMSWWorker, MSWHttpResponseFactory, MSWWorker, NodeMSWWorker } from './types/msw';
|
|
17
19
|
import { LocalHttpInterceptorWorkerOptions } from './types/options';
|
|
18
20
|
|
|
21
|
+
interface HttpHandler {
|
|
22
|
+
baseURL: string;
|
|
23
|
+
method: HttpMethod;
|
|
24
|
+
pathPattern: RegExp;
|
|
25
|
+
interceptor: AnyHttpInterceptorClient;
|
|
26
|
+
createResponse: (context: HttpResponseFactoryContext) => Promise<Response>;
|
|
27
|
+
}
|
|
28
|
+
|
|
19
29
|
class LocalHttpInterceptorWorker extends HttpInterceptorWorker {
|
|
20
30
|
private internalWorker?: MSWWorker;
|
|
21
31
|
|
|
22
|
-
private
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
32
|
+
private httpHandlersByMethod: {
|
|
33
|
+
[Method in HttpMethod]: HttpHandler[];
|
|
34
|
+
} = {
|
|
35
|
+
GET: [],
|
|
36
|
+
POST: [],
|
|
37
|
+
PATCH: [],
|
|
38
|
+
PUT: [],
|
|
39
|
+
DELETE: [],
|
|
40
|
+
HEAD: [],
|
|
41
|
+
OPTIONS: [],
|
|
42
|
+
};
|
|
28
43
|
|
|
29
44
|
constructor(_options: LocalHttpInterceptorWorkerOptions) {
|
|
30
45
|
super();
|
|
31
|
-
|
|
32
|
-
this.defaultHttpHandler = http.all('*', async (context) => {
|
|
33
|
-
const request = context.request satisfies Request as HttpRequest;
|
|
34
|
-
return this.bypassOrRejectUnhandledRequest(request);
|
|
35
|
-
});
|
|
36
46
|
}
|
|
37
47
|
|
|
38
48
|
get type() {
|
|
@@ -52,13 +62,21 @@ class LocalHttpInterceptorWorker extends HttpInterceptorWorker {
|
|
|
52
62
|
}
|
|
53
63
|
|
|
54
64
|
private createInternalWorker() {
|
|
65
|
+
const mswHttpHandler = http.all('*', async (context) => {
|
|
66
|
+
const request = context.request satisfies Request as HttpRequest;
|
|
67
|
+
const response = await this.createResponseForRequest(request);
|
|
68
|
+
return response;
|
|
69
|
+
});
|
|
70
|
+
|
|
55
71
|
if (isServerSide() && 'setupServer' in mswNode) {
|
|
56
|
-
return mswNode.setupServer(
|
|
72
|
+
return mswNode.setupServer(mswHttpHandler);
|
|
57
73
|
}
|
|
74
|
+
|
|
58
75
|
/* istanbul ignore else -- @preserve */
|
|
59
76
|
if (isClientSide() && 'setupWorker' in mswBrowser) {
|
|
60
|
-
return mswBrowser.setupWorker(
|
|
77
|
+
return mswBrowser.setupWorker(mswHttpHandler);
|
|
61
78
|
}
|
|
79
|
+
|
|
62
80
|
/* istanbul ignore next -- @preserve
|
|
63
81
|
* Ignoring because checking unknown platforms is not configured in our test setup. */
|
|
64
82
|
throw new UnknownHttpInterceptorPlatformError();
|
|
@@ -142,45 +160,79 @@ class LocalHttpInterceptorWorker extends HttpInterceptorWorker {
|
|
|
142
160
|
use<Schema extends HttpSchema>(
|
|
143
161
|
interceptor: HttpInterceptorClient<Schema>,
|
|
144
162
|
method: HttpMethod,
|
|
145
|
-
|
|
163
|
+
path: string,
|
|
146
164
|
createResponse: MSWHttpResponseFactory,
|
|
147
165
|
) {
|
|
148
|
-
|
|
166
|
+
if (!this.isRunning) {
|
|
167
|
+
throw new NotRunningHttpInterceptorError();
|
|
168
|
+
}
|
|
149
169
|
|
|
150
|
-
|
|
151
|
-
excludeURLParams(url);
|
|
152
|
-
validateURLPathParams(url);
|
|
170
|
+
validatePathParams(path);
|
|
153
171
|
|
|
154
|
-
const
|
|
155
|
-
const request = context.request as HttpRequest;
|
|
156
|
-
const requestClone = request.clone();
|
|
172
|
+
const methodHandlers = this.httpHandlersByMethod[method];
|
|
157
173
|
|
|
158
|
-
|
|
174
|
+
const handler: HttpHandler = {
|
|
175
|
+
baseURL: interceptor.baseURLAsString,
|
|
176
|
+
method,
|
|
177
|
+
pathPattern: createParametrizedPathPattern(path),
|
|
178
|
+
interceptor,
|
|
179
|
+
createResponse: async (context) => {
|
|
180
|
+
const request = context.request as HttpRequest;
|
|
181
|
+
const requestClone = request.clone();
|
|
159
182
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
183
|
+
let response: HttpResponse | null = null;
|
|
184
|
+
|
|
185
|
+
try {
|
|
186
|
+
response = await createResponse({ ...context, request });
|
|
187
|
+
} catch (error) {
|
|
188
|
+
console.error(error);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (!response) {
|
|
192
|
+
return this.bypassOrRejectUnhandledRequest(requestClone);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (context.request.method === 'HEAD') {
|
|
196
|
+
return new Response(null, {
|
|
197
|
+
status: response.status,
|
|
198
|
+
statusText: response.statusText,
|
|
199
|
+
headers: response.headers,
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return response;
|
|
204
|
+
},
|
|
205
|
+
};
|
|
165
206
|
|
|
166
|
-
|
|
167
|
-
|
|
207
|
+
methodHandlers.push(handler);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
private async createResponseForRequest(request: HttpRequest) {
|
|
211
|
+
const methodHandlers = this.httpHandlersByMethod[request.method as HttpMethod];
|
|
212
|
+
|
|
213
|
+
const requestURL = excludeURLParams(new URL(request.url));
|
|
214
|
+
const requestURLAsString = requestURL.href === `${requestURL.origin}/` ? requestURL.origin : requestURL.href;
|
|
215
|
+
|
|
216
|
+
for (let handlerIndex = methodHandlers.length - 1; handlerIndex >= 0; handlerIndex--) {
|
|
217
|
+
const handler = methodHandlers[handlerIndex];
|
|
218
|
+
const matchesBaseURL = requestURLAsString.startsWith(handler.baseURL);
|
|
219
|
+
|
|
220
|
+
if (!matchesBaseURL) {
|
|
221
|
+
continue;
|
|
168
222
|
}
|
|
169
223
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
});
|
|
224
|
+
const requestPath = requestURLAsString.replace(handler.baseURL, '');
|
|
225
|
+
const matchesPath = handler.pathPattern.test(requestPath);
|
|
226
|
+
|
|
227
|
+
if (!matchesPath) {
|
|
228
|
+
continue;
|
|
176
229
|
}
|
|
177
230
|
|
|
231
|
+
const response = await handler.createResponse({ request });
|
|
178
232
|
return response;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
this.internalWorkerOrThrow.use(httpHandler);
|
|
233
|
+
}
|
|
182
234
|
|
|
183
|
-
this.
|
|
235
|
+
return this.bypassOrRejectUnhandledRequest(request);
|
|
184
236
|
}
|
|
185
237
|
|
|
186
238
|
private async bypassOrRejectUnhandledRequest(request: HttpRequest) {
|
|
@@ -198,22 +250,33 @@ class LocalHttpInterceptorWorker extends HttpInterceptorWorker {
|
|
|
198
250
|
|
|
199
251
|
clearHandlers() {
|
|
200
252
|
this.internalWorkerOrThrow.resetHandlers();
|
|
201
|
-
|
|
253
|
+
|
|
254
|
+
for (const handlers of Object.values(this.httpHandlersByMethod)) {
|
|
255
|
+
handlers.length = 0;
|
|
256
|
+
}
|
|
202
257
|
}
|
|
203
258
|
|
|
204
259
|
clearInterceptorHandlers<Schema extends HttpSchema>(interceptor: HttpInterceptorClient<Schema>) {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
this.internalWorkerOrThrow.resetHandlers();
|
|
260
|
+
if (!this.isRunning) {
|
|
261
|
+
throw new NotRunningHttpInterceptorError();
|
|
262
|
+
}
|
|
209
263
|
|
|
210
|
-
for (const
|
|
211
|
-
|
|
264
|
+
for (const methodHandlers of Object.values(this.httpHandlersByMethod)) {
|
|
265
|
+
const groupToRemoveIndex = methodHandlers.findIndex((group) => group.interceptor === interceptor);
|
|
266
|
+
removeArrayIndex(methodHandlers, groupToRemoveIndex);
|
|
212
267
|
}
|
|
213
268
|
}
|
|
214
269
|
|
|
215
270
|
get interceptorsWithHandlers() {
|
|
216
|
-
|
|
271
|
+
const interceptors = new Set<AnyHttpInterceptorClient>();
|
|
272
|
+
|
|
273
|
+
for (const handlers of Object.values(this.httpHandlersByMethod)) {
|
|
274
|
+
for (const handler of handlers) {
|
|
275
|
+
interceptors.add(handler.interceptor);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return Array.from(interceptors);
|
|
217
280
|
}
|
|
218
281
|
}
|
|
219
282
|
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import validateURLPathParams from '@zimic/utils/url/validateURLPathParams';
|
|
1
|
+
import { HttpMethod, HttpSchema } from '@zimic/http';
|
|
2
|
+
import validatePathParams from '@zimic/utils/url/validatePathParams';
|
|
4
3
|
|
|
5
4
|
import { HttpHandlerCommit, InterceptorServerWebSocketSchema } from '@/server/types/schema';
|
|
6
5
|
import { importCrypto } from '@/utils/crypto';
|
|
@@ -14,15 +13,17 @@ import UnknownHttpInterceptorPlatformError from '../interceptor/errors/UnknownHt
|
|
|
14
13
|
import HttpInterceptorClient, { AnyHttpInterceptorClient } from '../interceptor/HttpInterceptorClient';
|
|
15
14
|
import { HttpInterceptorPlatform } from '../interceptor/types/options';
|
|
16
15
|
import HttpInterceptorWorker from './HttpInterceptorWorker';
|
|
17
|
-
import {
|
|
16
|
+
import { HttpResponseFactoryContext } from './types/http';
|
|
17
|
+
import { MSWHttpResponseFactory } from './types/msw';
|
|
18
18
|
import { RemoteHttpInterceptorWorkerOptions } from './types/options';
|
|
19
19
|
|
|
20
20
|
interface HttpHandler {
|
|
21
21
|
id: string;
|
|
22
|
-
|
|
22
|
+
baseURL: string;
|
|
23
23
|
method: HttpMethod;
|
|
24
|
+
path: string;
|
|
24
25
|
interceptor: AnyHttpInterceptorClient;
|
|
25
|
-
createResponse: (context:
|
|
26
|
+
createResponse: (context: HttpResponseFactoryContext) => Promise<Response | null>;
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
class RemoteHttpInterceptorWorker extends HttpInterceptorWorker {
|
|
@@ -132,26 +133,22 @@ class RemoteHttpInterceptorWorker extends HttpInterceptorWorker {
|
|
|
132
133
|
async use<Schema extends HttpSchema>(
|
|
133
134
|
interceptor: HttpInterceptorClient<Schema>,
|
|
134
135
|
method: HttpMethod,
|
|
135
|
-
|
|
136
|
+
path: string,
|
|
136
137
|
createResponse: MSWHttpResponseFactory,
|
|
137
138
|
) {
|
|
138
139
|
if (!this.isRunning) {
|
|
139
140
|
throw new NotRunningHttpInterceptorError();
|
|
140
141
|
}
|
|
141
142
|
|
|
142
|
-
|
|
143
|
+
validatePathParams(path);
|
|
143
144
|
|
|
144
|
-
const
|
|
145
|
-
excludeURLParams(url);
|
|
146
|
-
validateURLPathParams(url);
|
|
145
|
+
const crypto = await importCrypto();
|
|
147
146
|
|
|
148
147
|
const handler: HttpHandler = {
|
|
149
148
|
id: crypto.randomUUID(),
|
|
150
|
-
|
|
151
|
-
base: interceptor.baseURLAsString,
|
|
152
|
-
full: url.toString(),
|
|
153
|
-
},
|
|
149
|
+
baseURL: interceptor.baseURLAsString,
|
|
154
150
|
method,
|
|
151
|
+
path,
|
|
155
152
|
interceptor,
|
|
156
153
|
async createResponse(context) {
|
|
157
154
|
const response = await createResponse(context);
|
|
@@ -163,8 +160,9 @@ class RemoteHttpInterceptorWorker extends HttpInterceptorWorker {
|
|
|
163
160
|
|
|
164
161
|
await this.webSocketClient.request('interceptors/workers/commit', {
|
|
165
162
|
id: handler.id,
|
|
166
|
-
|
|
167
|
-
method,
|
|
163
|
+
baseURL: handler.baseURL,
|
|
164
|
+
method: handler.method,
|
|
165
|
+
path: handler.path,
|
|
168
166
|
});
|
|
169
167
|
}
|
|
170
168
|
|
|
@@ -194,8 +192,9 @@ class RemoteHttpInterceptorWorker extends HttpInterceptorWorker {
|
|
|
194
192
|
if (this.webSocketClient.isRunning) {
|
|
195
193
|
const groupsToRecommit = Array.from<HttpHandler, HttpHandlerCommit>(this.httpHandlers.values(), (handler) => ({
|
|
196
194
|
id: handler.id,
|
|
197
|
-
|
|
195
|
+
baseURL: handler.baseURL,
|
|
198
196
|
method: handler.method,
|
|
197
|
+
path: handler.path,
|
|
199
198
|
}));
|
|
200
199
|
|
|
201
200
|
await this.webSocketClient.request('interceptors/workers/reset', groupsToRecommit);
|
|
@@ -1,18 +1,16 @@
|
|
|
1
|
-
import { HttpResponse, HttpBody
|
|
1
|
+
import { HttpResponse, HttpBody } from '@zimic/http';
|
|
2
2
|
import { PossiblePromise } from '@zimic/utils/types';
|
|
3
3
|
import type { HttpHandler as MSWHandler } from 'msw';
|
|
4
4
|
import type { SetupWorker as BrowserMSWWorker } from 'msw/browser';
|
|
5
5
|
import type { SetupServer as NodeMSWWorker } from 'msw/node';
|
|
6
6
|
|
|
7
|
+
import { HttpResponseFactoryContext } from './http';
|
|
8
|
+
|
|
7
9
|
export type { MSWHandler, NodeMSWWorker, BrowserMSWWorker };
|
|
8
10
|
|
|
9
11
|
export type MSWWorker = NodeMSWWorker | BrowserMSWWorker;
|
|
10
12
|
|
|
11
|
-
export interface MSWHttpResponseFactoryContext<Body extends HttpBody = HttpBody> {
|
|
12
|
-
request: HttpRequest<Body>;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
13
|
export type MSWHttpResponseFactory<
|
|
16
14
|
RequestBody extends HttpBody = HttpBody,
|
|
17
15
|
ResponseBody extends HttpBody = HttpBody,
|
|
18
|
-
> = (context:
|
|
16
|
+
> = (context: HttpResponseFactoryContext<RequestBody>) => PossiblePromise<HttpResponse<ResponseBody> | null>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { normalizeNodeRequest, sendNodeResponse } from '@whatwg-node/server';
|
|
2
2
|
import { HttpRequest, HttpMethod } from '@zimic/http';
|
|
3
|
-
import
|
|
3
|
+
import createParametrizedPathPattern from '@zimic/utils/url/createParametrizedPathPattern';
|
|
4
4
|
import excludeURLParams from '@zimic/utils/url/excludeURLParams';
|
|
5
5
|
import { createServer, Server as HttpServer, IncomingMessage, ServerResponse } from 'http';
|
|
6
6
|
import type { WebSocket as Socket } from 'isomorphic-ws';
|
|
@@ -29,10 +29,8 @@ import { getFetchAPI } from './utils/fetch';
|
|
|
29
29
|
|
|
30
30
|
interface HttpHandler {
|
|
31
31
|
id: string;
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
fullRegex: RegExp;
|
|
35
|
-
};
|
|
32
|
+
baseURL: string;
|
|
33
|
+
pathPattern: RegExp;
|
|
36
34
|
socket: Socket;
|
|
37
35
|
}
|
|
38
36
|
|
|
@@ -45,7 +43,7 @@ class InterceptorServer implements PublicInterceptorServer {
|
|
|
45
43
|
logUnhandledRequests: boolean;
|
|
46
44
|
tokensDirectory?: string;
|
|
47
45
|
|
|
48
|
-
private
|
|
46
|
+
private httpHandlersByMethod: {
|
|
49
47
|
[Method in HttpMethod]: HttpHandler[];
|
|
50
48
|
} = {
|
|
51
49
|
GET: [],
|
|
@@ -218,18 +216,13 @@ class InterceptorServer implements PublicInterceptorServer {
|
|
|
218
216
|
return {};
|
|
219
217
|
};
|
|
220
218
|
|
|
221
|
-
private registerHttpHandler({ id,
|
|
222
|
-
const handlerGroups = this.
|
|
223
|
-
|
|
224
|
-
const fullURL = new URL(url.full);
|
|
225
|
-
excludeURLParams(fullURL);
|
|
219
|
+
private registerHttpHandler({ id, baseURL, method, path }: HttpHandlerCommit, socket: Socket) {
|
|
220
|
+
const handlerGroups = this.httpHandlersByMethod[method];
|
|
226
221
|
|
|
227
222
|
handlerGroups.push({
|
|
228
223
|
id,
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
fullRegex: createRegExpFromURL(fullURL.toString()),
|
|
232
|
-
},
|
|
224
|
+
baseURL,
|
|
225
|
+
pathPattern: createParametrizedPathPattern(path),
|
|
233
226
|
socket,
|
|
234
227
|
});
|
|
235
228
|
}
|
|
@@ -248,7 +241,7 @@ class InterceptorServer implements PublicInterceptorServer {
|
|
|
248
241
|
}
|
|
249
242
|
|
|
250
243
|
private removeHttpHandlersBySocket(socket: Socket) {
|
|
251
|
-
for (const handlerGroups of Object.values(this.
|
|
244
|
+
for (const handlerGroups of Object.values(this.httpHandlersByMethod)) {
|
|
252
245
|
const socketIndex = handlerGroups.findIndex((handlerGroup) => handlerGroup.socket === socket);
|
|
253
246
|
removeArrayIndex(handlerGroups, socketIndex);
|
|
254
247
|
}
|
|
@@ -319,17 +312,25 @@ class InterceptorServer implements PublicInterceptorServer {
|
|
|
319
312
|
};
|
|
320
313
|
|
|
321
314
|
private async createResponseForRequest(request: SerializedHttpRequest) {
|
|
322
|
-
const methodHandlers = this.
|
|
315
|
+
const methodHandlers = this.httpHandlersByMethod[request.method as HttpMethod];
|
|
323
316
|
|
|
324
|
-
const requestURL = excludeURLParams(new URL(request.url))
|
|
317
|
+
const requestURL = excludeURLParams(new URL(request.url));
|
|
318
|
+
const requestURLAsString = requestURL.href === `${requestURL.origin}/` ? requestURL.origin : requestURL.href;
|
|
325
319
|
|
|
326
320
|
let matchedSomeInterceptor = false;
|
|
327
321
|
|
|
328
|
-
for (let
|
|
329
|
-
const handler = methodHandlers[
|
|
322
|
+
for (let handlerIndex = methodHandlers.length - 1; handlerIndex >= 0; handlerIndex--) {
|
|
323
|
+
const handler = methodHandlers[handlerIndex];
|
|
324
|
+
const matchesBaseURL = requestURLAsString.startsWith(handler.baseURL);
|
|
325
|
+
|
|
326
|
+
if (!matchesBaseURL) {
|
|
327
|
+
continue;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const requestPath = requestURLAsString.replace(handler.baseURL, '');
|
|
331
|
+
const matchesPath = handler.pathPattern.test(requestPath);
|
|
330
332
|
|
|
331
|
-
|
|
332
|
-
if (!matchesHandlerURL) {
|
|
333
|
+
if (!matchesPath) {
|
|
333
334
|
continue;
|
|
334
335
|
}
|
|
335
336
|
|
|
@@ -403,12 +404,9 @@ class InterceptorServer implements PublicInterceptorServer {
|
|
|
403
404
|
}
|
|
404
405
|
|
|
405
406
|
private findHttpHandlerByRequestBaseURL(request: HttpRequest) {
|
|
406
|
-
const methodHandlers = this.
|
|
407
|
-
|
|
408
|
-
const handler = methodHandlers.findLast((handler) => {
|
|
409
|
-
return request.url.startsWith(handler.url.base);
|
|
410
|
-
});
|
|
407
|
+
const methodHandlers = this.httpHandlersByMethod[request.method as HttpMethod];
|
|
411
408
|
|
|
409
|
+
const handler = methodHandlers.findLast((handler) => request.url.startsWith(handler.baseURL));
|
|
412
410
|
return handler;
|
|
413
411
|
}
|
|
414
412
|
}
|
|
@@ -5,8 +5,9 @@ import { WebSocketSchema } from '@/webSocket/types';
|
|
|
5
5
|
|
|
6
6
|
export interface HttpHandlerCommit {
|
|
7
7
|
id: string;
|
|
8
|
-
|
|
8
|
+
baseURL: string;
|
|
9
9
|
method: HttpMethod;
|
|
10
|
+
path: string;
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
export type InterceptorServerWebSocketSchema = WebSocketSchema<{
|