@zimic/interceptor 1.5.1 → 1.5.2-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-3WCEMV5U.js → chunk-6YD4TBYJ.js} +13 -4
- package/dist/chunk-6YD4TBYJ.js.map +1 -0
- package/dist/{chunk-YQNDAJTN.mjs → chunk-MCLPYYCY.mjs} +13 -4
- package/dist/chunk-MCLPYYCY.mjs.map +1 -0
- package/dist/cli.js +16 -16
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +2 -2
- package/dist/cli.mjs.map +1 -1
- package/dist/http.d.ts +24 -8
- package/dist/http.js +368 -289
- package/dist/http.js.map +1 -1
- package/dist/http.mjs +367 -288
- package/dist/http.mjs.map +1 -1
- package/dist/server.js +6 -6
- package/dist/server.mjs +1 -1
- package/package.json +10 -10
- package/src/http/errors/HttpTimesCheckError.ts +9 -9
- package/src/http/index.ts +6 -1
- package/src/http/interceptor/HttpInterceptorImplementation.ts +74 -21
- package/src/http/interceptor/HttpInterceptorStore.ts +11 -13
- package/src/http/interceptor/LocalHttpInterceptor.ts +1 -10
- package/src/http/interceptor/RemoteHttpInterceptor.ts +16 -22
- package/src/http/interceptor/errors/UnknownHttpInterceptorPlatformError.ts +3 -1
- package/src/http/interceptor/factory.ts +3 -0
- package/src/http/interceptor/types/options.ts +1 -1
- package/src/http/interceptorWorker/HttpInterceptorWorker.ts +14 -3
- package/src/http/interceptorWorker/LocalHttpInterceptorWorker.ts +25 -93
- package/src/http/requestHandler/HttpRequestHandlerImplementation.ts +5 -9
- package/src/http/requestHandler/LocalHttpRequestHandler.ts +2 -5
- package/src/http/requestHandler/RemoteHttpRequestHandler.ts +17 -20
- package/src/http/requestHandler/types/public.ts +3 -8
- package/src/http/requestHandler/types/requests.ts +31 -3
- package/src/http/utils/logging.ts +54 -0
- package/src/interceptor/LocalMSWWorkerStore.ts +123 -0
- package/src/{http/interceptorWorker → interceptor}/types/msw.ts +1 -2
- package/src/server/utils/auth.ts +1 -1
- package/src/utils/fetch.ts +2 -2
- package/src/utils/logging.ts +0 -54
- package/src/utils/webSocket/WebSocketHandler.ts +1 -1
- package/dist/chunk-3WCEMV5U.js.map +0 -1
- package/dist/chunk-YQNDAJTN.mjs.map +0 -1
- /package/src/{http/interceptorWorker → interceptor}/errors/UnregisteredBrowserServiceWorkerError.ts +0 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { HttpRequest, HttpResponse, HttpMethod, HttpSchema, HttpHeadersInit, HttpBody } from '@zimic/http';
|
|
2
|
-
import { createCachedDynamicImport } from '@zimic/utils/import';
|
|
3
2
|
import { createRegexFromPath, excludeNonPathParams, validatePathParams } from '@zimic/utils/url';
|
|
4
|
-
import { SharedOptions as MSWWorkerSharedOptions, bypass, http, passthrough } from 'msw';
|
|
3
|
+
import { SharedOptions as MSWWorkerSharedOptions, HttpHandler as MSWHttpHandler, bypass, http, passthrough } from 'msw';
|
|
5
4
|
|
|
5
|
+
import LocalMSWWorkerStore from '@/interceptor/LocalMSWWorkerStore';
|
|
6
|
+
import { MSWWorker } from '@/interceptor/types/msw';
|
|
6
7
|
import { removeArrayIndex } from '@/utils/arrays';
|
|
7
|
-
import { isClientSide, isServerSide } from '@/utils/environment';
|
|
8
8
|
|
|
9
9
|
import NotRunningHttpInterceptorError from '../interceptor/errors/NotRunningHttpInterceptorError';
|
|
10
10
|
import UnknownHttpInterceptorPlatformError from '../interceptor/errors/UnknownHttpInterceptorPlatformError';
|
|
@@ -12,15 +12,10 @@ import HttpInterceptorImplementation, {
|
|
|
12
12
|
AnyHttpInterceptorImplementation,
|
|
13
13
|
} from '../interceptor/HttpInterceptorImplementation';
|
|
14
14
|
import { UnhandledRequestStrategy } from '../interceptor/types/options';
|
|
15
|
-
import UnregisteredBrowserServiceWorkerError from './errors/UnregisteredBrowserServiceWorkerError';
|
|
16
15
|
import HttpInterceptorWorker from './HttpInterceptorWorker';
|
|
17
16
|
import { HttpResponseFactory, HttpResponseFactoryContext } from './types/http';
|
|
18
|
-
import { BrowserMSWWorker, MSWHttpHandler, MSWWorker, NodeMSWWorker } from './types/msw';
|
|
19
17
|
import { LocalHttpInterceptorWorkerOptions } from './types/options';
|
|
20
18
|
|
|
21
|
-
const importMSWNode = createCachedDynamicImport(() => import('msw/node'));
|
|
22
|
-
const importMSWBrowser = createCachedDynamicImport(() => import('msw/browser'));
|
|
23
|
-
|
|
24
19
|
interface HttpHandler {
|
|
25
20
|
baseURL: string;
|
|
26
21
|
method: HttpMethod;
|
|
@@ -30,11 +25,6 @@ interface HttpHandler {
|
|
|
30
25
|
}
|
|
31
26
|
|
|
32
27
|
class LocalHttpInterceptorWorker extends HttpInterceptorWorker {
|
|
33
|
-
// Re-creating MSW workers may cause issues, so we should keep a single worker instance, even if all interceptor
|
|
34
|
-
// workers are stopped. See https://github.com/mswjs/msw/issues/2597.
|
|
35
|
-
private static mswWorker?: MSWWorker;
|
|
36
|
-
static isMSWWorkerRunning = false;
|
|
37
|
-
|
|
38
28
|
private mswHttpHandler?: MSWHttpHandler;
|
|
39
29
|
|
|
40
30
|
private httpHandlersByMethod: {
|
|
@@ -49,14 +39,12 @@ class LocalHttpInterceptorWorker extends HttpInterceptorWorker {
|
|
|
49
39
|
OPTIONS: [],
|
|
50
40
|
};
|
|
51
41
|
|
|
42
|
+
private store = new LocalMSWWorkerStore();
|
|
43
|
+
|
|
52
44
|
constructor(_options: LocalHttpInterceptorWorkerOptions) {
|
|
53
45
|
super();
|
|
54
46
|
}
|
|
55
47
|
|
|
56
|
-
get class() {
|
|
57
|
-
return LocalHttpInterceptorWorker;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
48
|
get type() {
|
|
61
49
|
return 'local' as const;
|
|
62
50
|
}
|
|
@@ -64,44 +52,20 @@ class LocalHttpInterceptorWorker extends HttpInterceptorWorker {
|
|
|
64
52
|
get mswWorkerOrThrow() {
|
|
65
53
|
/* istanbul ignore if -- @preserve
|
|
66
54
|
* Trying to access the internal worker when it does not exist should not happen. */
|
|
67
|
-
if (!this.
|
|
55
|
+
if (!this.store.mswWorker) {
|
|
68
56
|
throw new NotRunningHttpInterceptorError();
|
|
69
57
|
}
|
|
70
|
-
return this.
|
|
58
|
+
return this.store.mswWorker;
|
|
71
59
|
}
|
|
72
60
|
|
|
73
61
|
async getMSWWorkerOrCreate() {
|
|
74
|
-
this.
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
/* istanbul ignore else -- @preserve
|
|
83
|
-
* We still check if we actually imported the server module in case our `isServerSide()` check returns true, but
|
|
84
|
-
* the environment actually resolves the browser module. */
|
|
85
|
-
if ('setupServer' in mswNode) {
|
|
86
|
-
return mswNode.setupServer();
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/* istanbul ignore else -- @preserve */
|
|
91
|
-
if (isClientSide()) {
|
|
92
|
-
const mswBrowser = await importMSWBrowser();
|
|
93
|
-
|
|
94
|
-
/* istanbul ignore else -- @preserve
|
|
95
|
-
* We still check if we actually imported the browser module in case our `isClientSide()` check returns true, but
|
|
96
|
-
* the environment actually resolves the server module. */
|
|
97
|
-
if ('setupWorker' in mswBrowser) {
|
|
98
|
-
return mswBrowser.setupWorker();
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/* istanbul ignore next -- @preserve
|
|
103
|
-
* Ignoring because checking unknown platforms is not configured in our test setup. */
|
|
104
|
-
throw new UnknownHttpInterceptorPlatformError();
|
|
62
|
+
return this.store.getMSWWorkerOrCreate({
|
|
63
|
+
createUnknownPlatformError:
|
|
64
|
+
// The unknown platform error is created here to prevent the store from relying on HTTP-specific resources.
|
|
65
|
+
/* istanbul ignore next -- @preserve
|
|
66
|
+
* Unknown runtime platforms are not part of the package test matrix. */
|
|
67
|
+
() => new UnknownHttpInterceptorPlatformError(),
|
|
68
|
+
});
|
|
105
69
|
}
|
|
106
70
|
|
|
107
71
|
async start() {
|
|
@@ -122,45 +86,23 @@ class LocalHttpInterceptorWorker extends HttpInterceptorWorker {
|
|
|
122
86
|
|
|
123
87
|
if (this.isInternalBrowserWorker(mswWorker)) {
|
|
124
88
|
this.platform = 'browser';
|
|
125
|
-
|
|
126
|
-
// Even after https://github.com/mswjs/msw/issues/2714 was fixed, there are collateral effects preventing us
|
|
127
|
-
// from restarting the global browser worker even if unused. Restarting it causes interception issues in MSW.
|
|
128
|
-
if (!this.class.isMSWWorkerRunning) {
|
|
129
|
-
await this.startInBrowser(mswWorker, sharedOptions);
|
|
130
|
-
this.class.isMSWWorkerRunning = true;
|
|
131
|
-
}
|
|
132
89
|
} else {
|
|
133
90
|
this.platform = 'node';
|
|
134
|
-
|
|
135
|
-
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
await this.store.startMSWWorker(mswWorker, sharedOptions);
|
|
95
|
+
} catch (error) {
|
|
96
|
+
const newMSWHandlers = mswWorker.listHandlers().filter((handler) => handler !== this.mswHttpHandler);
|
|
97
|
+
mswWorker.resetHandlers(...newMSWHandlers);
|
|
98
|
+
this.mswHttpHandler = undefined;
|
|
99
|
+
throw error;
|
|
136
100
|
}
|
|
137
101
|
|
|
138
102
|
this.isRunning = true;
|
|
139
103
|
});
|
|
140
104
|
}
|
|
141
105
|
|
|
142
|
-
private async startInBrowser(mswWorker: BrowserMSWWorker, sharedOptions: MSWWorkerSharedOptions) {
|
|
143
|
-
try {
|
|
144
|
-
await mswWorker.start({ ...sharedOptions, quiet: true });
|
|
145
|
-
} catch (error) {
|
|
146
|
-
this.handleBrowserWorkerStartError(error);
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
private handleBrowserWorkerStartError(error: unknown) {
|
|
151
|
-
/* istanbul ignore else -- @preserve
|
|
152
|
-
* Since we start the internal worker once and do not stop it, tests may not be able exercise this branch. */
|
|
153
|
-
if (UnregisteredBrowserServiceWorkerError.matchesRawError(error)) {
|
|
154
|
-
throw new UnregisteredBrowserServiceWorkerError();
|
|
155
|
-
} else {
|
|
156
|
-
throw error;
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
private startInNode(mswWorker: NodeMSWWorker, sharedOptions: MSWWorkerSharedOptions) {
|
|
161
|
-
mswWorker.listen(sharedOptions);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
106
|
async stop() {
|
|
165
107
|
await super.sharedStop(async () => {
|
|
166
108
|
const mswWorker = await this.getMSWWorkerOrCreate();
|
|
@@ -170,25 +112,15 @@ class LocalHttpInterceptorWorker extends HttpInterceptorWorker {
|
|
|
170
112
|
const newMSWHandlers = mswWorker.listHandlers().filter((handler) => handler !== this.mswHttpHandler);
|
|
171
113
|
mswWorker.resetHandlers(...newMSWHandlers);
|
|
172
114
|
|
|
173
|
-
|
|
174
|
-
// Even after https://github.com/mswjs/msw/issues/2714 was fixed, restarting browser workers causes interception
|
|
175
|
-
// issues, so we keep it running and just remove our handlers.
|
|
176
|
-
} else {
|
|
177
|
-
this.stopInNode(mswWorker);
|
|
178
|
-
this.class.isMSWWorkerRunning = false;
|
|
179
|
-
}
|
|
115
|
+
this.store.stopMSWWorker(mswWorker);
|
|
180
116
|
|
|
181
117
|
this.mswHttpHandler = undefined;
|
|
182
118
|
this.isRunning = false;
|
|
183
119
|
});
|
|
184
120
|
}
|
|
185
121
|
|
|
186
|
-
private stopInNode(mswWorker: NodeMSWWorker) {
|
|
187
|
-
mswWorker.close();
|
|
188
|
-
}
|
|
189
|
-
|
|
190
122
|
private isInternalBrowserWorker(worker: MSWWorker) {
|
|
191
|
-
return
|
|
123
|
+
return this.store.isInternalBrowserWorker(worker);
|
|
192
124
|
}
|
|
193
125
|
|
|
194
126
|
hasInternalBrowserWorker() {
|
|
@@ -24,10 +24,10 @@ import { InternalHttpRequestHandler } from './types/public';
|
|
|
24
24
|
import {
|
|
25
25
|
HttpInterceptorRequest,
|
|
26
26
|
HttpInterceptorResponse,
|
|
27
|
-
|
|
28
|
-
HttpRequestHandlerResponseDeclarationFactory,
|
|
27
|
+
HttpRequestHandlerResponseComputedDeclaration,
|
|
29
28
|
HttpRequestHandlerResponseDelayFactory,
|
|
30
29
|
InterceptedHttpInterceptorRequest,
|
|
30
|
+
HttpRequestHandlerResponseDeclarationInput,
|
|
31
31
|
} from './types/requests';
|
|
32
32
|
import {
|
|
33
33
|
HttpRequestHandlerRestriction,
|
|
@@ -70,7 +70,7 @@ class HttpRequestHandlerImplementation<
|
|
|
70
70
|
StatusCode
|
|
71
71
|
>[] = [];
|
|
72
72
|
|
|
73
|
-
private createResponseDeclaration?:
|
|
73
|
+
private createResponseDeclaration?: HttpRequestHandlerResponseComputedDeclaration<
|
|
74
74
|
Path,
|
|
75
75
|
Default<Schema[Path][Method]>,
|
|
76
76
|
StatusCode
|
|
@@ -113,9 +113,7 @@ class HttpRequestHandlerImplementation<
|
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
respond<NewStatusCode extends HttpStatusCode>(
|
|
116
|
-
declaration:
|
|
117
|
-
| HttpRequestHandlerResponseDeclaration<Default<Schema[Path][Method]>, NewStatusCode>
|
|
118
|
-
| HttpRequestHandlerResponseDeclarationFactory<Path, Default<Schema[Path][Method]>, NewStatusCode>,
|
|
116
|
+
declaration: HttpRequestHandlerResponseDeclarationInput<Path, Default<Schema[Path][Method]>, NewStatusCode>,
|
|
119
117
|
): HttpRequestHandlerImplementation<Schema, Method, Path, NewStatusCode> {
|
|
120
118
|
const newThis = this as unknown as HttpRequestHandlerImplementation<Schema, Method, Path, NewStatusCode>;
|
|
121
119
|
|
|
@@ -134,9 +132,7 @@ class HttpRequestHandlerImplementation<
|
|
|
134
132
|
}
|
|
135
133
|
|
|
136
134
|
private isResponseDeclarationFactory(
|
|
137
|
-
declaration:
|
|
138
|
-
| HttpRequestHandlerResponseDeclaration<Default<Schema[Path][Method]>>
|
|
139
|
-
| HttpRequestHandlerResponseDeclarationFactory<Path, Default<Schema[Path][Method]>>,
|
|
135
|
+
declaration: HttpRequestHandlerResponseDeclarationInput<Path, Default<Schema[Path][Method]>>,
|
|
140
136
|
) {
|
|
141
137
|
return typeof declaration === 'function';
|
|
142
138
|
}
|
|
@@ -7,8 +7,7 @@ import { InternalHttpRequestHandler } from './types/public';
|
|
|
7
7
|
import {
|
|
8
8
|
HttpInterceptorRequest,
|
|
9
9
|
HttpInterceptorResponse,
|
|
10
|
-
|
|
11
|
-
HttpRequestHandlerResponseDeclarationFactory,
|
|
10
|
+
HttpRequestHandlerResponseDeclarationInput,
|
|
12
11
|
HttpRequestHandlerResponseDelayFactory,
|
|
13
12
|
InterceptedHttpInterceptorRequest,
|
|
14
13
|
} from './types/requests';
|
|
@@ -50,9 +49,7 @@ class LocalHttpRequestHandler<
|
|
|
50
49
|
}
|
|
51
50
|
|
|
52
51
|
respond<NewStatusCode extends HttpStatusCode>(
|
|
53
|
-
declaration:
|
|
54
|
-
| HttpRequestHandlerResponseDeclaration<Default<Schema[Path][Method]>, NewStatusCode>
|
|
55
|
-
| HttpRequestHandlerResponseDeclarationFactory<Path, Default<Schema[Path][Method]>, NewStatusCode>,
|
|
52
|
+
declaration: HttpRequestHandlerResponseDeclarationInput<Path, Default<Schema[Path][Method]>, NewStatusCode>,
|
|
56
53
|
): LocalHttpRequestHandler<Schema, Method, Path, NewStatusCode> {
|
|
57
54
|
this.implementation.respond(declaration);
|
|
58
55
|
|
|
@@ -10,14 +10,13 @@ import {
|
|
|
10
10
|
import {
|
|
11
11
|
HttpInterceptorRequest,
|
|
12
12
|
HttpInterceptorResponse,
|
|
13
|
-
|
|
14
|
-
HttpRequestHandlerResponseDeclarationFactory,
|
|
13
|
+
HttpRequestHandlerResponseDeclarationInput,
|
|
15
14
|
HttpRequestHandlerResponseDelayFactory,
|
|
16
15
|
InterceptedHttpInterceptorRequest,
|
|
17
16
|
} from './types/requests';
|
|
18
17
|
import { HttpRequestHandlerRestriction } from './types/restrictions';
|
|
19
18
|
|
|
20
|
-
const
|
|
19
|
+
const PENDING_PROPERTIES = new Set<string | symbol>(['then'] satisfies (keyof Promise<unknown>)[]);
|
|
21
20
|
|
|
22
21
|
class RemoteHttpRequestHandler<
|
|
23
22
|
Schema extends HttpSchema,
|
|
@@ -31,7 +30,7 @@ class RemoteHttpRequestHandler<
|
|
|
31
30
|
|
|
32
31
|
private syncPromises: Promise<unknown>[] = [];
|
|
33
32
|
|
|
34
|
-
private
|
|
33
|
+
private pending: this;
|
|
35
34
|
private synced: this;
|
|
36
35
|
|
|
37
36
|
constructor(
|
|
@@ -40,7 +39,7 @@ class RemoteHttpRequestHandler<
|
|
|
40
39
|
path: Path,
|
|
41
40
|
) {
|
|
42
41
|
this.implementation = new HttpRequestHandlerImplementation(interceptor, method, path, this);
|
|
43
|
-
this.
|
|
42
|
+
this.pending = this;
|
|
44
43
|
this.synced = this.createSyncedProxy();
|
|
45
44
|
}
|
|
46
45
|
|
|
@@ -63,7 +62,7 @@ class RemoteHttpRequestHandler<
|
|
|
63
62
|
}
|
|
64
63
|
|
|
65
64
|
private shouldBeHiddenPropertyWhenSynced(property: string | symbol) {
|
|
66
|
-
return
|
|
65
|
+
return PENDING_PROPERTIES.has(property);
|
|
67
66
|
}
|
|
68
67
|
|
|
69
68
|
get method() {
|
|
@@ -74,30 +73,28 @@ class RemoteHttpRequestHandler<
|
|
|
74
73
|
return this.implementation.path;
|
|
75
74
|
}
|
|
76
75
|
|
|
77
|
-
with(restriction: HttpRequestHandlerRestriction<Schema, Method, Path>)
|
|
76
|
+
with(restriction: HttpRequestHandlerRestriction<Schema, Method, Path>) {
|
|
78
77
|
this.implementation.with(restriction);
|
|
79
|
-
return this.
|
|
78
|
+
return this.pending;
|
|
80
79
|
}
|
|
81
80
|
|
|
82
81
|
delay(
|
|
83
82
|
minMilliseconds: number | HttpRequestHandlerResponseDelayFactory<Path, Default<Schema[Path][Method]>>,
|
|
84
83
|
maxMilliseconds?: number,
|
|
85
|
-
)
|
|
84
|
+
) {
|
|
86
85
|
this.implementation.delay(minMilliseconds, maxMilliseconds);
|
|
87
|
-
return this.
|
|
86
|
+
return this.pending;
|
|
88
87
|
}
|
|
89
88
|
|
|
90
89
|
respond<NewStatusCode extends HttpStatusCode>(
|
|
91
|
-
declaration:
|
|
92
|
-
| HttpRequestHandlerResponseDeclaration<Default<Schema[Path][Method]>, NewStatusCode>
|
|
93
|
-
| HttpRequestHandlerResponseDeclarationFactory<Path, Default<Schema[Path][Method]>, NewStatusCode>,
|
|
90
|
+
declaration: HttpRequestHandlerResponseDeclarationInput<Path, Default<Schema[Path][Method]>, NewStatusCode>,
|
|
94
91
|
): RemoteHttpRequestHandler<Schema, Method, Path, NewStatusCode> {
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
return
|
|
92
|
+
const pending = this.pending as unknown as RemoteHttpRequestHandler<Schema, Method, Path, NewStatusCode>;
|
|
93
|
+
pending.implementation.respond(declaration);
|
|
94
|
+
return pending;
|
|
98
95
|
}
|
|
99
96
|
|
|
100
|
-
times(minNumberOfRequests: number, maxNumberOfRequests?: number)
|
|
97
|
+
times(minNumberOfRequests: number, maxNumberOfRequests?: number) {
|
|
101
98
|
this.implementation.times(minNumberOfRequests, maxNumberOfRequests);
|
|
102
99
|
return this;
|
|
103
100
|
}
|
|
@@ -113,9 +110,9 @@ class RemoteHttpRequestHandler<
|
|
|
113
110
|
});
|
|
114
111
|
}
|
|
115
112
|
|
|
116
|
-
clear()
|
|
113
|
+
clear() {
|
|
117
114
|
this.implementation.clear();
|
|
118
|
-
return this.
|
|
115
|
+
return this.pending;
|
|
119
116
|
}
|
|
120
117
|
|
|
121
118
|
get requests(): readonly InterceptedHttpInterceptorRequest<Path, Default<Schema[Path][Method]>, StatusCode>[] {
|
|
@@ -172,7 +169,7 @@ class RemoteHttpRequestHandler<
|
|
|
172
169
|
.then(() => {
|
|
173
170
|
this.syncPromises = this.syncPromises.filter((promise) => !promisesToWait.has(promise));
|
|
174
171
|
|
|
175
|
-
return this.isSynced ? this.synced : this.
|
|
172
|
+
return this.isSynced ? this.synced : this.pending;
|
|
176
173
|
})
|
|
177
174
|
.then(onFulfilled, onRejected);
|
|
178
175
|
}
|
|
@@ -9,8 +9,7 @@ import { Default, PossiblePromise } from '@zimic/utils/types';
|
|
|
9
9
|
|
|
10
10
|
import HttpRequestHandlerImplementation from '../HttpRequestHandlerImplementation';
|
|
11
11
|
import {
|
|
12
|
-
|
|
13
|
-
HttpRequestHandlerResponseDeclarationFactory,
|
|
12
|
+
HttpRequestHandlerResponseDeclarationInput,
|
|
14
13
|
HttpRequestHandlerResponseDelayFactory,
|
|
15
14
|
InterceptedHttpInterceptorRequest,
|
|
16
15
|
} from './requests';
|
|
@@ -65,9 +64,7 @@ export interface LocalHttpRequestHandler<
|
|
|
65
64
|
|
|
66
65
|
/** @see {@link https://zimic.dev/docs/interceptor/api/http-request-handler#handlerrespond `handler.respond()` API reference} */
|
|
67
66
|
respond: <NewStatusCode extends HttpResponseSchemaStatusCode<Default<Default<Schema[Path][Method]>['response']>>>(
|
|
68
|
-
declaration:
|
|
69
|
-
| HttpRequestHandlerResponseDeclaration<Default<Schema[Path][Method]>, NewStatusCode>
|
|
70
|
-
| HttpRequestHandlerResponseDeclarationFactory<Path, Default<Schema[Path][Method]>, NewStatusCode>,
|
|
67
|
+
declaration: HttpRequestHandlerResponseDeclarationInput<Path, Default<Schema[Path][Method]>, NewStatusCode>,
|
|
71
68
|
) => LocalHttpRequestHandler<Schema, Method, Path, NewStatusCode>;
|
|
72
69
|
|
|
73
70
|
/** @see {@link https://zimic.dev/docs/interceptor/api/http-request-handler#handlertimes `handler.times()` API reference} */
|
|
@@ -117,9 +114,7 @@ export interface SyncedRemoteHttpRequestHandler<
|
|
|
117
114
|
|
|
118
115
|
/** @see {@link https://zimic.dev/docs/interceptor/api/http-request-handler#handlerrespond `handler.respond()` API reference} */
|
|
119
116
|
respond: <NewStatusCode extends HttpResponseSchemaStatusCode<Default<Default<Schema[Path][Method]>['response']>>>(
|
|
120
|
-
declaration:
|
|
121
|
-
| HttpRequestHandlerResponseDeclaration<Default<Schema[Path][Method]>, NewStatusCode>
|
|
122
|
-
| HttpRequestHandlerResponseDeclarationFactory<Path, Default<Schema[Path][Method]>, NewStatusCode>,
|
|
117
|
+
declaration: HttpRequestHandlerResponseDeclarationInput<Path, Default<Schema[Path][Method]>, NewStatusCode>,
|
|
123
118
|
) => PendingRemoteHttpRequestHandler<Schema, Method, Path, NewStatusCode>;
|
|
124
119
|
|
|
125
120
|
/** @see {@link https://zimic.dev/docs/interceptor/api/http-request-handler#handlertimes `handler.times()` API reference} */
|
|
@@ -62,19 +62,47 @@ export interface HttpRequestHandlerActionResponseDeclaration {
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
/** @see {@link https://zimic.dev/docs/interceptor/api/http-request-handler#handlerrespond `handler.respond()` API reference} */
|
|
65
|
-
export type
|
|
65
|
+
export type HttpRequestHandlerResponseStaticDeclaration<
|
|
66
66
|
MethodSchema extends HttpMethodSchema = HttpMethodSchema,
|
|
67
67
|
StatusCode extends HttpStatusCode = HttpStatusCode,
|
|
68
68
|
> = HttpRequestHandlerStatusResponseDeclaration<MethodSchema, StatusCode> | HttpRequestHandlerActionResponseDeclaration;
|
|
69
69
|
|
|
70
|
+
/**
|
|
71
|
+
* @deprecated Use `HttpRequestHandlerResponseStaticDeclaration` instead.
|
|
72
|
+
* @see {@link https://zimic.dev/docs/interceptor/api/http-request-handler#handlerrespond `handler.respond()` API reference}
|
|
73
|
+
*/
|
|
74
|
+
export type HttpRequestHandlerResponseDeclaration<
|
|
75
|
+
MethodSchema extends HttpMethodSchema = HttpMethodSchema,
|
|
76
|
+
StatusCode extends HttpStatusCode = HttpStatusCode,
|
|
77
|
+
> = HttpRequestHandlerResponseStaticDeclaration<MethodSchema, StatusCode>;
|
|
78
|
+
|
|
70
79
|
/** @see {@link https://zimic.dev/docs/interceptor/api/http-request-handler#handlerrespond `handler.respond()` API reference} */
|
|
71
|
-
export type
|
|
80
|
+
export type HttpRequestHandlerResponseComputedDeclaration<
|
|
72
81
|
Path extends string,
|
|
73
82
|
MethodSchema extends HttpMethodSchema,
|
|
74
83
|
StatusCode extends HttpStatusCode = HttpStatusCode,
|
|
75
84
|
> = (
|
|
76
85
|
request: Omit<HttpInterceptorRequest<Path, MethodSchema>, 'response'>,
|
|
77
|
-
) => PossiblePromise<
|
|
86
|
+
) => PossiblePromise<HttpRequestHandlerResponseStaticDeclaration<MethodSchema, StatusCode>>;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* @deprecated Use `HttpRequestHandlerResponseComputedDeclaration` instead.
|
|
90
|
+
* @see {@link https://zimic.dev/docs/interceptor/api/http-request-handler#handlerrespond `handler.respond()` API reference}
|
|
91
|
+
*/
|
|
92
|
+
export type HttpRequestHandlerResponseDeclarationFactory<
|
|
93
|
+
Path extends string,
|
|
94
|
+
MethodSchema extends HttpMethodSchema,
|
|
95
|
+
StatusCode extends HttpStatusCode = HttpStatusCode,
|
|
96
|
+
> = HttpRequestHandlerResponseComputedDeclaration<Path, MethodSchema, StatusCode>;
|
|
97
|
+
|
|
98
|
+
/** @see {@link https://zimic.dev/docs/interceptor/api/http-request-handler#handlerrespond `handler.respond()` API reference} */
|
|
99
|
+
export type HttpRequestHandlerResponseDeclarationInput<
|
|
100
|
+
Path extends string,
|
|
101
|
+
MethodSchema extends HttpMethodSchema,
|
|
102
|
+
StatusCode extends HttpStatusCode = HttpStatusCode,
|
|
103
|
+
> =
|
|
104
|
+
| HttpRequestHandlerResponseStaticDeclaration<MethodSchema, StatusCode>
|
|
105
|
+
| HttpRequestHandlerResponseComputedDeclaration<Path, MethodSchema, StatusCode>;
|
|
78
106
|
|
|
79
107
|
/** @see {@link https://zimic.dev/docs/interceptor/api/http-request-handler#handlerdelay `handler.delay()` API reference} */
|
|
80
108
|
export type HttpRequestHandlerResponseDelayFactory<Path extends string, MethodSchema extends HttpMethodSchema> = (
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { HttpFormData, HttpHeaders, HttpSearchParams } from '@zimic/http';
|
|
2
|
+
|
|
3
|
+
function stringifyHttpJSONToLog(value: unknown): string {
|
|
4
|
+
return JSON.stringify(
|
|
5
|
+
value,
|
|
6
|
+
(_key, value) => {
|
|
7
|
+
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
|
8
|
+
return stringifyHttpValueToLog(value, {
|
|
9
|
+
fallback: (value) => value as string,
|
|
10
|
+
});
|
|
11
|
+
},
|
|
12
|
+
2,
|
|
13
|
+
)
|
|
14
|
+
.replace(/\n\s*/g, ' ')
|
|
15
|
+
.replace(/"(File { name: '.*?', type: '.*?', size: \d*? })"/g, '$1')
|
|
16
|
+
.replace(/"(Blob { type: '.*?', size: \d*? })"/g, '$1');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function stringifyHttpValueToLog(
|
|
20
|
+
value: unknown,
|
|
21
|
+
options: {
|
|
22
|
+
fallback?: (value: unknown) => string;
|
|
23
|
+
includeClassName?: { searchParams?: boolean };
|
|
24
|
+
} = {},
|
|
25
|
+
): string {
|
|
26
|
+
const { fallback = stringifyHttpJSONToLog, includeClassName } = options;
|
|
27
|
+
|
|
28
|
+
if (value === null || value === undefined || typeof value !== 'object') {
|
|
29
|
+
return String(value);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (value instanceof HttpHeaders) {
|
|
33
|
+
return stringifyHttpValueToLog(value.toObject());
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (value instanceof HttpSearchParams) {
|
|
37
|
+
const prefix = (includeClassName?.searchParams ?? false) ? 'URLSearchParams ' : '';
|
|
38
|
+
return `${prefix}${stringifyHttpValueToLog(value.toObject())}`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (value instanceof HttpFormData) {
|
|
42
|
+
return `FormData ${stringifyHttpValueToLog(value.toObject())}`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (value instanceof File) {
|
|
46
|
+
return `File { name: '${value.name}', type: '${value.type}', size: ${value.size} }`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (value instanceof Blob) {
|
|
50
|
+
return `Blob { type: '${value.type}', size: ${value.size} }`;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return fallback(value);
|
|
54
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { createCachedDynamicImport } from '@zimic/utils/import';
|
|
2
|
+
import type { SharedOptions as MSWWorkerSharedOptions } from 'msw';
|
|
3
|
+
|
|
4
|
+
import { isClientSide, isServerSide } from '@/utils/environment';
|
|
5
|
+
|
|
6
|
+
import UnregisteredBrowserServiceWorkerError from './errors/UnregisteredBrowserServiceWorkerError';
|
|
7
|
+
import type { BrowserMSWWorker, MSWWorker } from './types/msw';
|
|
8
|
+
|
|
9
|
+
const importMSWNode = createCachedDynamicImport(() => import('msw/node'));
|
|
10
|
+
const importMSWBrowser = createCachedDynamicImport(() => import('msw/browser'));
|
|
11
|
+
|
|
12
|
+
class LocalMSWWorkerStore {
|
|
13
|
+
private static mswWorker?: MSWWorker;
|
|
14
|
+
|
|
15
|
+
private static creatingMSWWorkerPromise?: Promise<MSWWorker>;
|
|
16
|
+
private static startingMSWWorkerPromise?: Promise<void>;
|
|
17
|
+
|
|
18
|
+
private static numberOfRunningWorkers = 0;
|
|
19
|
+
|
|
20
|
+
static isMSWWorkerRunning = false;
|
|
21
|
+
|
|
22
|
+
private class = LocalMSWWorkerStore;
|
|
23
|
+
|
|
24
|
+
get mswWorker() {
|
|
25
|
+
return this.class.mswWorker;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async getMSWWorkerOrCreate(options: { createUnknownPlatformError: () => Error }) {
|
|
29
|
+
if (this.class.mswWorker) {
|
|
30
|
+
return this.class.mswWorker;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
this.class.creatingMSWWorkerPromise ??= this.createMSWWorker(options)
|
|
34
|
+
.then((mswWorker) => {
|
|
35
|
+
this.class.mswWorker = mswWorker;
|
|
36
|
+
return mswWorker;
|
|
37
|
+
})
|
|
38
|
+
.finally(() => {
|
|
39
|
+
this.class.creatingMSWWorkerPromise = undefined;
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
return this.class.creatingMSWWorkerPromise;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
private async createMSWWorker(options: { createUnknownPlatformError: () => Error }) {
|
|
46
|
+
if (isServerSide()) {
|
|
47
|
+
const mswNode = await importMSWNode();
|
|
48
|
+
|
|
49
|
+
/* istanbul ignore else -- @preserve */
|
|
50
|
+
if ('setupServer' in mswNode) {
|
|
51
|
+
return mswNode.setupServer();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/* istanbul ignore else -- @preserve */
|
|
56
|
+
if (isClientSide()) {
|
|
57
|
+
const mswBrowser = await importMSWBrowser();
|
|
58
|
+
|
|
59
|
+
/* istanbul ignore else -- @preserve */
|
|
60
|
+
if ('setupWorker' in mswBrowser) {
|
|
61
|
+
return mswBrowser.setupWorker();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/* istanbul ignore next -- @preserve */
|
|
66
|
+
throw options.createUnknownPlatformError();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async startMSWWorker(mswWorker: MSWWorker, sharedOptions: MSWWorkerSharedOptions) {
|
|
70
|
+
if (!this.class.isMSWWorkerRunning) {
|
|
71
|
+
this.class.startingMSWWorkerPromise ??= this.startMSWWorkerOnce(mswWorker, sharedOptions)
|
|
72
|
+
.then(() => {
|
|
73
|
+
this.class.isMSWWorkerRunning = true;
|
|
74
|
+
})
|
|
75
|
+
.finally(() => {
|
|
76
|
+
this.class.startingMSWWorkerPromise = undefined;
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
await this.class.startingMSWWorkerPromise;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
this.class.numberOfRunningWorkers++;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
private async startMSWWorkerOnce(mswWorker: MSWWorker, sharedOptions: MSWWorkerSharedOptions) {
|
|
86
|
+
if (this.isInternalBrowserWorker(mswWorker)) {
|
|
87
|
+
try {
|
|
88
|
+
await mswWorker.start({ ...sharedOptions, quiet: true });
|
|
89
|
+
} catch (error) {
|
|
90
|
+
/* istanbul ignore else -- @preserve
|
|
91
|
+
* Browser worker start errors other than unregistered service workers come from MSW internals. */
|
|
92
|
+
if (UnregisteredBrowserServiceWorkerError.matchesRawError(error)) {
|
|
93
|
+
throw new UnregisteredBrowserServiceWorkerError();
|
|
94
|
+
} else {
|
|
95
|
+
throw error;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
} else {
|
|
99
|
+
mswWorker.listen(sharedOptions);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
stopMSWWorker(mswWorker: MSWWorker) {
|
|
104
|
+
this.class.numberOfRunningWorkers = Math.max(this.class.numberOfRunningWorkers - 1, 0);
|
|
105
|
+
|
|
106
|
+
if (this.class.numberOfRunningWorkers > 0) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (this.isInternalBrowserWorker(mswWorker)) {
|
|
111
|
+
// Browser workers are kept running because restarting them can cause interception issues in MSW.
|
|
112
|
+
} else {
|
|
113
|
+
mswWorker.close();
|
|
114
|
+
this.class.isMSWWorkerRunning = false;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
isInternalBrowserWorker(worker: MSWWorker): worker is BrowserMSWWorker {
|
|
119
|
+
return 'start' in worker && 'stop' in worker;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export default LocalMSWWorkerStore;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import type { HttpHandler as MSWHttpHandler } from 'msw';
|
|
2
1
|
import type { SetupWorker as BrowserMSWWorker } from 'msw/browser';
|
|
3
2
|
import type { SetupServer as NodeMSWWorker } from 'msw/node';
|
|
4
3
|
|
|
5
|
-
export type {
|
|
4
|
+
export type { NodeMSWWorker, BrowserMSWWorker };
|
|
6
5
|
|
|
7
6
|
export type MSWWorker = NodeMSWWorker | BrowserMSWWorker;
|
package/src/server/utils/auth.ts
CHANGED
|
@@ -162,7 +162,7 @@ export async function readInterceptorTokenFromFile(
|
|
|
162
162
|
|
|
163
163
|
const tokenFileContentAsString = await fs.promises.readFile(tokenFilePath, { encoding: 'utf-8' });
|
|
164
164
|
|
|
165
|
-
const validation = interceptorTokenFileContentSchema.safeParse(JSON.parse(tokenFileContentAsString)
|
|
165
|
+
const validation = interceptorTokenFileContentSchema.safeParse(JSON.parse(tokenFileContentAsString));
|
|
166
166
|
|
|
167
167
|
if (!validation.success) {
|
|
168
168
|
throw new InvalidInterceptorTokenFileError(tokenFilePath, validation.error.message);
|
package/src/utils/fetch.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { JSONValue } from '@zimic/
|
|
1
|
+
import type { JSONValue } from '@zimic/utils/types';
|
|
2
2
|
|
|
3
|
-
import { UnhandledRequestStrategy } from '@/http';
|
|
3
|
+
import type { UnhandledRequestStrategy } from '@/http';
|
|
4
4
|
import HttpInterceptorWorker from '@/http/interceptorWorker/HttpInterceptorWorker';
|
|
5
5
|
|
|
6
6
|
import { convertArrayBufferToBase64, convertBase64ToArrayBuffer } from './data';
|