msw 2.13.3 → 2.13.4
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/cli/init.js +1 -1
- package/lib/browser/index.js +200 -56
- package/lib/browser/index.js.map +1 -1
- package/lib/browser/index.mjs +200 -56
- package/lib/browser/index.mjs.map +1 -1
- package/lib/core/{HttpResponse-DlRR1D-f.d.mts → HttpResponse-BF4NGRsf.d.mts} +1 -1
- package/lib/core/{HttpResponse-CksOMVAa.d.ts → HttpResponse-yukpQS4a.d.ts} +1 -1
- package/lib/core/HttpResponse.d.mts +1 -1
- package/lib/core/HttpResponse.d.ts +1 -1
- package/lib/core/experimental/compat.d.mts +2 -2
- package/lib/core/experimental/compat.d.ts +2 -2
- package/lib/core/experimental/compat.js +1 -0
- package/lib/core/experimental/compat.js.map +1 -1
- package/lib/core/experimental/compat.mjs +1 -0
- package/lib/core/experimental/compat.mjs.map +1 -1
- package/lib/core/experimental/define-network.d.mts +2 -2
- package/lib/core/experimental/define-network.d.ts +2 -2
- package/lib/core/experimental/define-network.js +4 -0
- package/lib/core/experimental/define-network.js.map +1 -1
- package/lib/core/experimental/define-network.mjs +6 -0
- package/lib/core/experimental/define-network.mjs.map +1 -1
- package/lib/core/experimental/frames/http-frame.d.mts +2 -2
- package/lib/core/experimental/frames/http-frame.d.ts +2 -2
- package/lib/core/experimental/frames/http-frame.js +2 -0
- package/lib/core/experimental/frames/http-frame.js.map +1 -1
- package/lib/core/experimental/frames/http-frame.mjs +5 -1
- package/lib/core/experimental/frames/http-frame.mjs.map +1 -1
- package/lib/core/experimental/frames/network-frame.d.mts +2 -2
- package/lib/core/experimental/frames/network-frame.d.ts +2 -2
- package/lib/core/experimental/frames/websocket-frame.d.mts +2 -2
- package/lib/core/experimental/frames/websocket-frame.d.ts +2 -2
- package/lib/core/experimental/frames/websocket-frame.js +2 -0
- package/lib/core/experimental/frames/websocket-frame.js.map +1 -1
- package/lib/core/experimental/frames/websocket-frame.mjs +2 -0
- package/lib/core/experimental/frames/websocket-frame.mjs.map +1 -1
- package/lib/core/experimental/handlers-controller.d.mts +1 -1
- package/lib/core/experimental/handlers-controller.d.ts +1 -1
- package/lib/core/experimental/handlers-controller.js +3 -1
- package/lib/core/experimental/handlers-controller.js.map +1 -1
- package/lib/core/experimental/handlers-controller.mjs +3 -1
- package/lib/core/experimental/handlers-controller.mjs.map +1 -1
- package/lib/core/experimental/index.d.mts +2 -2
- package/lib/core/experimental/index.d.ts +2 -2
- package/lib/core/experimental/index.js +0 -1
- package/lib/core/experimental/index.js.map +1 -1
- package/lib/core/experimental/index.mjs +1 -2
- package/lib/core/experimental/index.mjs.map +1 -1
- package/lib/core/experimental/on-unhandled-frame.d.mts +2 -2
- package/lib/core/experimental/on-unhandled-frame.d.ts +2 -2
- package/lib/core/experimental/on-unhandled-frame.js +1 -0
- package/lib/core/experimental/on-unhandled-frame.js.map +1 -1
- package/lib/core/experimental/on-unhandled-frame.mjs +1 -0
- package/lib/core/experimental/on-unhandled-frame.mjs.map +1 -1
- package/lib/core/experimental/setup-api.d.mts +1 -1
- package/lib/core/experimental/setup-api.d.ts +1 -1
- package/lib/core/experimental/setup-api.js +1 -0
- package/lib/core/experimental/setup-api.js.map +1 -1
- package/lib/core/experimental/setup-api.mjs +1 -0
- package/lib/core/experimental/setup-api.mjs.map +1 -1
- package/lib/core/experimental/sources/interceptor-source.d.mts +2 -2
- package/lib/core/experimental/sources/interceptor-source.d.ts +2 -2
- package/lib/core/experimental/sources/interceptor-source.js.map +1 -1
- package/lib/core/experimental/sources/interceptor-source.mjs +1 -3
- package/lib/core/experimental/sources/interceptor-source.mjs.map +1 -1
- package/lib/core/experimental/sources/network-source.d.mts +2 -2
- package/lib/core/experimental/sources/network-source.d.ts +2 -2
- package/lib/core/experimental/sources/network-source.js +1 -0
- package/lib/core/experimental/sources/network-source.js.map +1 -1
- package/lib/core/experimental/sources/network-source.mjs +2 -0
- package/lib/core/experimental/sources/network-source.mjs.map +1 -1
- package/lib/core/getResponse.d.mts +1 -1
- package/lib/core/getResponse.d.ts +1 -1
- package/lib/core/graphql.d.mts +1 -1
- package/lib/core/graphql.d.ts +1 -1
- package/lib/core/graphql.js +1 -0
- package/lib/core/graphql.js.map +1 -1
- package/lib/core/graphql.mjs +2 -0
- package/lib/core/graphql.mjs.map +1 -1
- package/lib/core/handlers/GraphQLHandler.d.mts +1 -1
- package/lib/core/handlers/GraphQLHandler.d.ts +1 -1
- package/lib/core/handlers/GraphQLHandler.js +1 -0
- package/lib/core/handlers/GraphQLHandler.js.map +1 -1
- package/lib/core/handlers/GraphQLHandler.mjs +4 -1
- package/lib/core/handlers/GraphQLHandler.mjs.map +1 -1
- package/lib/core/handlers/HttpHandler.d.mts +1 -1
- package/lib/core/handlers/HttpHandler.d.ts +1 -1
- package/lib/core/handlers/HttpHandler.js +1 -0
- package/lib/core/handlers/HttpHandler.js.map +1 -1
- package/lib/core/handlers/HttpHandler.mjs +1 -0
- package/lib/core/handlers/HttpHandler.mjs.map +1 -1
- package/lib/core/handlers/RequestHandler.d.mts +1 -1
- package/lib/core/handlers/RequestHandler.d.ts +1 -1
- package/lib/core/handlers/RequestHandler.js +1 -0
- package/lib/core/handlers/RequestHandler.js.map +1 -1
- package/lib/core/handlers/RequestHandler.mjs +2 -0
- package/lib/core/handlers/RequestHandler.mjs.map +1 -1
- package/lib/core/http.d.mts +1 -1
- package/lib/core/http.d.ts +1 -1
- package/lib/core/http.js +1 -0
- package/lib/core/http.js.map +1 -1
- package/lib/core/http.mjs +2 -0
- package/lib/core/http.mjs.map +1 -1
- package/lib/core/index.d.mts +1 -1
- package/lib/core/index.d.ts +1 -1
- package/lib/core/passthrough.d.mts +1 -1
- package/lib/core/passthrough.d.ts +1 -1
- package/lib/core/sse.d.mts +4 -18
- package/lib/core/sse.d.ts +4 -18
- package/lib/core/sse.js +105 -45
- package/lib/core/sse.js.map +1 -1
- package/lib/core/sse.mjs +105 -45
- package/lib/core/sse.mjs.map +1 -1
- package/lib/core/utils/HttpResponse/decorators.d.mts +1 -1
- package/lib/core/utils/HttpResponse/decorators.d.ts +1 -1
- package/lib/core/utils/cookieStore.js.map +1 -1
- package/lib/core/utils/cookieStore.mjs.map +1 -1
- package/lib/core/utils/executeHandlers.d.mts +1 -1
- package/lib/core/utils/executeHandlers.d.ts +1 -1
- package/lib/core/utils/executeHandlers.js +1 -0
- package/lib/core/utils/executeHandlers.js.map +1 -1
- package/lib/core/utils/executeHandlers.mjs +1 -0
- package/lib/core/utils/executeHandlers.mjs.map +1 -1
- package/lib/core/utils/handleRequest.d.mts +1 -1
- package/lib/core/utils/handleRequest.d.ts +1 -1
- package/lib/core/utils/handleRequest.js.map +1 -1
- package/lib/core/utils/handleRequest.mjs.map +1 -1
- package/lib/core/utils/internal/isHandlerKind.d.mts +1 -1
- package/lib/core/utils/internal/isHandlerKind.d.ts +1 -1
- package/lib/core/utils/internal/parseGraphQLRequest.d.mts +1 -1
- package/lib/core/utils/internal/parseGraphQLRequest.d.ts +1 -1
- package/lib/core/utils/internal/parseMultipartData.d.mts +1 -1
- package/lib/core/utils/internal/parseMultipartData.d.ts +1 -1
- package/lib/core/utils/internal/parseMultipartData.js +1 -0
- package/lib/core/utils/internal/parseMultipartData.js.map +1 -1
- package/lib/core/utils/internal/parseMultipartData.mjs +1 -0
- package/lib/core/utils/internal/parseMultipartData.mjs.map +1 -1
- package/lib/core/utils/internal/pipeEvents.js +1 -0
- package/lib/core/utils/internal/pipeEvents.js.map +1 -1
- package/lib/core/utils/internal/pipeEvents.mjs +1 -0
- package/lib/core/utils/internal/pipeEvents.mjs.map +1 -1
- package/lib/core/utils/internal/requestHandlerUtils.d.mts +1 -1
- package/lib/core/utils/internal/requestHandlerUtils.d.ts +1 -1
- package/lib/core/utils/internal/requestHandlerUtils.js.map +1 -1
- package/lib/core/utils/internal/requestHandlerUtils.mjs.map +1 -1
- package/lib/core/ws/WebSocketClientManager.js.map +1 -1
- package/lib/core/ws/WebSocketClientManager.mjs.map +1 -1
- package/lib/core/ws/WebSocketIndexedDBClientStore.js +1 -0
- package/lib/core/ws/WebSocketIndexedDBClientStore.js.map +1 -1
- package/lib/core/ws/WebSocketIndexedDBClientStore.mjs +1 -0
- package/lib/core/ws/WebSocketIndexedDBClientStore.mjs.map +1 -1
- package/lib/core/ws/WebSocketMemoryClientStore.js +1 -0
- package/lib/core/ws/WebSocketMemoryClientStore.js.map +1 -1
- package/lib/core/ws/WebSocketMemoryClientStore.mjs +1 -0
- package/lib/core/ws/WebSocketMemoryClientStore.mjs.map +1 -1
- package/lib/core/ws/handleWebSocketEvent.d.mts +1 -1
- package/lib/core/ws/handleWebSocketEvent.d.ts +1 -1
- package/lib/core/ws/handleWebSocketEvent.js.map +1 -1
- package/lib/core/ws/handleWebSocketEvent.mjs.map +1 -1
- package/lib/core/ws.js.map +1 -1
- package/lib/core/ws.mjs.map +1 -1
- package/lib/iife/index.js +6300 -6076
- package/lib/iife/index.js.map +1 -1
- package/lib/mockServiceWorker.js +1 -1
- package/lib/native/index.js.map +1 -1
- package/lib/native/index.mjs.map +1 -1
- package/lib/node/index.js.map +1 -1
- package/lib/node/index.mjs.map +1 -1
- package/lib/shims/cookie.js +152 -62
- package/lib/shims/cookie.mjs +152 -62
- package/package.json +33 -40
- package/src/browser/glossary.ts +1 -1
- package/src/browser/setup-worker.ts +2 -2
- package/src/browser/sources/service-worker-source.ts +125 -28
- package/src/browser/utils/deserializeRequest.ts +0 -1
- package/src/browser/utils/should-invalidate-worker.test.ts +122 -0
- package/src/browser/utils/should-invalidate-worker.ts +13 -0
- package/src/browser/utils/workerChannel.ts +43 -21
- package/src/core/experimental/define-network.ts +10 -2
- package/src/core/experimental/frames/http-frame.test.ts +2 -1
- package/src/core/experimental/frames/http-frame.ts +6 -2
- package/src/core/experimental/frames/websocket-frame.test.ts +2 -4
- package/src/core/experimental/frames/websocket-frame.ts +3 -2
- package/src/core/experimental/handlers-controller.ts +1 -1
- package/src/core/experimental/index.ts +1 -1
- package/src/core/experimental/on-unhandled-frame.test.ts +2 -4
- package/src/core/experimental/setup-api.ts +3 -3
- package/src/core/experimental/sources/interceptor-source.ts +2 -6
- package/src/core/graphql.ts +8 -8
- package/src/core/handlers/GraphQLHandler.test.ts +3 -4
- package/src/core/handlers/GraphQLHandler.ts +15 -11
- package/src/core/handlers/HttpHandler.test.ts +3 -2
- package/src/core/handlers/HttpHandler.ts +7 -7
- package/src/core/handlers/RequestHandler.ts +5 -5
- package/src/core/http.ts +5 -5
- package/src/core/sse.ts +157 -56
- package/src/core/utils/cookieStore.ts +1 -1
- package/src/core/utils/executeHandlers.ts +2 -4
- package/src/core/utils/handleRequest.test.ts +5 -4
- package/src/core/utils/handleRequest.ts +3 -3
- package/src/core/utils/internal/parseGraphQLRequest.test.ts +2 -4
- package/src/core/utils/internal/parseMultipartData.ts +1 -1
- package/src/core/utils/internal/pipeEvents.ts +2 -1
- package/src/core/utils/internal/requestHandlerUtils.ts +1 -1
- package/src/core/utils/request/onUnhandledRequest.test.ts +2 -4
- package/src/core/ws/WebSocketClientManager.test.ts +2 -4
- package/src/core/ws/WebSocketClientManager.ts +1 -1
- package/src/core/ws/WebSocketIndexedDBClientStore.ts +3 -5
- package/src/core/ws/WebSocketMemoryClientStore.ts +3 -5
- package/src/core/ws/handleWebSocketEvent.ts +3 -3
- package/src/core/ws.ts +1 -1
- package/src/native/index.ts +2 -2
- package/src/node/async-handlers-controller.ts +2 -2
- package/src/node/setup-server-common.ts +4 -4
- package/src/node/setup-server.ts +2 -2
- package/lib/core/{network-frame-usYiHS0K.d.ts → on-unhandled-frame-BBR-P3kV.d.ts} +12 -12
- package/lib/core/{network-frame-B7A0ggXE.d.mts → on-unhandled-frame-Cr1KOZ0I.d.mts} +12 -12
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { invariant } from 'outvariant'
|
|
2
|
-
import { Emitter } from 'rettime'
|
|
2
|
+
import type { Emitter } from 'rettime'
|
|
3
3
|
import { DeferredPromise } from '@open-draft/deferred-promise'
|
|
4
4
|
import { FetchResponse } from '@mswjs/interceptors'
|
|
5
5
|
import { NetworkSource } from '#core/experimental/sources/network-source'
|
|
@@ -16,10 +16,12 @@ import {
|
|
|
16
16
|
supportsServiceWorker,
|
|
17
17
|
} from '../utils/supports'
|
|
18
18
|
import { getWorkerInstance } from '../utils/get-worker-instance'
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
19
|
+
import type { WorkerChannelEventMap } from '../utils/workerChannel'
|
|
20
|
+
import { WorkerChannel } from '../utils/workerChannel'
|
|
21
|
+
import type { FindWorker } from '../glossary'
|
|
21
22
|
import { deserializeRequest } from '../utils/deserializeRequest'
|
|
22
23
|
import { validateWorkerScope } from '../utils/validate-worker-scope'
|
|
24
|
+
import { shouldInvalidateWorker } from '../utils/should-invalidate-worker'
|
|
23
25
|
|
|
24
26
|
export interface ServiceWorkerSourceOptions {
|
|
25
27
|
quiet?: boolean
|
|
@@ -30,13 +32,13 @@ export interface ServiceWorkerSourceOptions {
|
|
|
30
32
|
findWorker?: FindWorker
|
|
31
33
|
}
|
|
32
34
|
|
|
33
|
-
type WorkerChannelRequestEvent = Emitter.
|
|
35
|
+
type WorkerChannelRequestEvent = Emitter.Event<
|
|
34
36
|
WorkerChannel,
|
|
35
37
|
'REQUEST',
|
|
36
38
|
WorkerChannelEventMap
|
|
37
39
|
>
|
|
38
40
|
|
|
39
|
-
type WorkerChannelResponseEvent = Emitter.
|
|
41
|
+
type WorkerChannelResponseEvent = Emitter.Event<
|
|
40
42
|
WorkerChannel,
|
|
41
43
|
'RESPONSE',
|
|
42
44
|
WorkerChannelEventMap
|
|
@@ -46,8 +48,31 @@ type WorkerChannelClient =
|
|
|
46
48
|
WorkerChannelEventMap['MOCKING_ENABLED']['data']['client']
|
|
47
49
|
|
|
48
50
|
export class ServiceWorkerSource extends NetworkSource<ServiceWorkerHttpNetworkFrame> {
|
|
51
|
+
static #current?: ServiceWorkerSource
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Create a new Service Worker source or reuse an existing one.
|
|
55
|
+
* These sources act as a singleton and only get recreated if the options change.
|
|
56
|
+
*/
|
|
57
|
+
public static async from(
|
|
58
|
+
options: ServiceWorkerSourceOptions,
|
|
59
|
+
): Promise<ServiceWorkerSource> {
|
|
60
|
+
if (ServiceWorkerSource.#current == null) {
|
|
61
|
+
ServiceWorkerSource.#current = new ServiceWorkerSource(options)
|
|
62
|
+
} else if (
|
|
63
|
+
shouldInvalidateWorker(ServiceWorkerSource.#current.#options, options)
|
|
64
|
+
) {
|
|
65
|
+
await ServiceWorkerSource.#current.terminate()
|
|
66
|
+
ServiceWorkerSource.#current = new ServiceWorkerSource(options)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return ServiceWorkerSource.#current
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
#options: ServiceWorkerSourceOptions
|
|
49
73
|
#frames: Map<string, ServiceWorkerHttpNetworkFrame>
|
|
50
74
|
#channel: WorkerChannel
|
|
75
|
+
#listenerController?: AbortController
|
|
51
76
|
#clientPromise?: Promise<WorkerChannelClient>
|
|
52
77
|
#keepAliveInterval?: number
|
|
53
78
|
#stoppedAt?: number
|
|
@@ -56,7 +81,7 @@ export class ServiceWorkerSource extends NetworkSource<ServiceWorkerHttpNetworkF
|
|
|
56
81
|
[ServiceWorker, ServiceWorkerRegistration]
|
|
57
82
|
>
|
|
58
83
|
|
|
59
|
-
constructor(
|
|
84
|
+
constructor(options: ServiceWorkerSourceOptions) {
|
|
60
85
|
super()
|
|
61
86
|
|
|
62
87
|
invariant(
|
|
@@ -64,17 +89,25 @@ export class ServiceWorkerSource extends NetworkSource<ServiceWorkerHttpNetworkF
|
|
|
64
89
|
'Failed to use Service Worker as the network source: the Service Worker API is not supported in this environment',
|
|
65
90
|
)
|
|
66
91
|
|
|
92
|
+
this.#options = options
|
|
67
93
|
this.#frames = new Map()
|
|
68
94
|
this.workerPromise = new DeferredPromise()
|
|
69
95
|
this.#channel = new WorkerChannel({
|
|
70
|
-
|
|
96
|
+
getWorker: () => this.workerPromise.then(([worker]) => worker),
|
|
71
97
|
})
|
|
72
98
|
}
|
|
73
99
|
|
|
74
100
|
public async enable(): Promise<ServiceWorkerRegistration> {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
101
|
+
/**
|
|
102
|
+
* @note The source is considered already running if the worker has been
|
|
103
|
+
* resolved AND `stop()` has not been called since. `workerPromise` is NOT
|
|
104
|
+
* reset on `disable()` so that the channel's `getWorker()` can keep
|
|
105
|
+
* resolving to the registered SW for post-stop passthrough replies.
|
|
106
|
+
*/
|
|
107
|
+
if (
|
|
108
|
+
this.workerPromise.state === 'fulfilled' &&
|
|
109
|
+
typeof this.#stoppedAt == 'undefined'
|
|
110
|
+
) {
|
|
78
111
|
devUtils.warn(
|
|
79
112
|
'Found a redundant "worker.start()" call. Note that starting the worker while mocking is already enabled will have no effect. Consider removing this "worker.start()" call.',
|
|
80
113
|
)
|
|
@@ -82,7 +115,11 @@ export class ServiceWorkerSource extends NetworkSource<ServiceWorkerHttpNetworkF
|
|
|
82
115
|
return this.workerPromise.then(([, registration]) => registration)
|
|
83
116
|
}
|
|
84
117
|
|
|
118
|
+
this.#stoppedAt = undefined
|
|
85
119
|
this.#channel.removeAllListeners()
|
|
120
|
+
this.#frames.clear()
|
|
121
|
+
|
|
122
|
+
this.#listenerController = new AbortController()
|
|
86
123
|
const [worker, registration] = await this.#startWorker()
|
|
87
124
|
|
|
88
125
|
if (worker.state !== 'activated') {
|
|
@@ -97,7 +134,9 @@ export class ServiceWorkerSource extends NetworkSource<ServiceWorkerHttpNetworkF
|
|
|
97
134
|
activationPromise.resolve()
|
|
98
135
|
}
|
|
99
136
|
},
|
|
100
|
-
{
|
|
137
|
+
{
|
|
138
|
+
signal: controller.signal,
|
|
139
|
+
},
|
|
101
140
|
)
|
|
102
141
|
|
|
103
142
|
await activationPromise
|
|
@@ -113,7 +152,7 @@ export class ServiceWorkerSource extends NetworkSource<ServiceWorkerHttpNetworkF
|
|
|
113
152
|
})
|
|
114
153
|
await clientConfirmationPromise
|
|
115
154
|
|
|
116
|
-
if (!this
|
|
155
|
+
if (!this.#options.quiet) {
|
|
117
156
|
this.#printStartMessage()
|
|
118
157
|
}
|
|
119
158
|
|
|
@@ -137,29 +176,70 @@ export class ServiceWorkerSource extends NetworkSource<ServiceWorkerHttpNetworkF
|
|
|
137
176
|
}
|
|
138
177
|
|
|
139
178
|
this.#stoppedAt = Date.now()
|
|
140
|
-
this.#frames.clear()
|
|
141
|
-
this.workerPromise = new DeferredPromise()
|
|
142
179
|
|
|
143
|
-
|
|
180
|
+
this.#listenerController?.abort()
|
|
181
|
+
this.#listenerController = undefined
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* @note Tell the Service Worker to drop this client from its active set
|
|
185
|
+
* so it stops forwarding REQUEST events here. `stoppedAt` still guards
|
|
186
|
+
* any requests the SW already forwarded before this message arrived.
|
|
187
|
+
*/
|
|
188
|
+
this.#channel.postMessage('CLIENT_CLOSED')
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* @note Do NOT reset `workerPromise` here. The channel must continue to
|
|
192
|
+
* resolve the currently registered SW so that any in-flight requests the
|
|
193
|
+
* worker forwards after `stop()` can be answered with `PASSTHROUGH` by
|
|
194
|
+
* `#handleRequest`. `#startWorker` swaps in a fresh deferred on re-enable.
|
|
195
|
+
*/
|
|
196
|
+
|
|
197
|
+
if (!this.#options.quiet) {
|
|
144
198
|
this.#printStopMessage()
|
|
145
199
|
}
|
|
146
200
|
}
|
|
147
201
|
|
|
202
|
+
/**
|
|
203
|
+
* Terminal teardown. Unregisters the Service Worker, tears down the channel,
|
|
204
|
+
* and clears timers. Called when the singleton is being replaced with one
|
|
205
|
+
* that has different options. The instance is not usable afterwards.
|
|
206
|
+
*/
|
|
207
|
+
public async terminate(): Promise<void> {
|
|
208
|
+
if (this.#keepAliveInterval != null) {
|
|
209
|
+
clearInterval(this.#keepAliveInterval)
|
|
210
|
+
this.#keepAliveInterval = undefined
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
this.#frames.clear()
|
|
214
|
+
this.#channel.terminate()
|
|
215
|
+
this.#listenerController?.abort()
|
|
216
|
+
this.#listenerController = undefined
|
|
217
|
+
|
|
218
|
+
if (this.workerPromise.state === 'fulfilled') {
|
|
219
|
+
const [, registration] = await this.workerPromise
|
|
220
|
+
await registration.unregister()
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (ServiceWorkerSource.#current === this) {
|
|
224
|
+
ServiceWorkerSource.#current = undefined
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
148
228
|
async #startWorker() {
|
|
149
229
|
if (this.#keepAliveInterval) {
|
|
150
230
|
clearInterval(this.#keepAliveInterval)
|
|
151
231
|
}
|
|
152
232
|
|
|
153
|
-
const workerUrl = this
|
|
233
|
+
const workerUrl = this.#options.serviceWorker.url
|
|
154
234
|
|
|
155
235
|
const [worker, registration] = await getWorkerInstance(
|
|
156
236
|
workerUrl,
|
|
157
|
-
this
|
|
158
|
-
this
|
|
237
|
+
this.#options.serviceWorker.options,
|
|
238
|
+
this.#options.findWorker || this.#defaultFindWorker,
|
|
159
239
|
)
|
|
160
240
|
|
|
161
241
|
if (worker == null) {
|
|
162
|
-
const missingWorkerMessage = this
|
|
242
|
+
const missingWorkerMessage = this.#options?.findWorker
|
|
163
243
|
? devUtils.formatMessage(
|
|
164
244
|
`Failed to locate the Service Worker registration using a custom "findWorker" predicate.
|
|
165
245
|
|
|
@@ -181,20 +261,37 @@ Please consider using a custom "serviceWorker.url" option to point to the actual
|
|
|
181
261
|
throw new Error(missingWorkerMessage)
|
|
182
262
|
}
|
|
183
263
|
|
|
184
|
-
this.workerPromise.
|
|
264
|
+
if (this.workerPromise.state === 'pending') {
|
|
265
|
+
this.workerPromise.resolve([worker, registration])
|
|
266
|
+
} else {
|
|
267
|
+
/**
|
|
268
|
+
* @note Re-enable after `stop()`: the previous `workerPromise` is already
|
|
269
|
+
* fulfilled and cannot be resolved again. Swap in a pre-resolved one so
|
|
270
|
+
* `getWorker()` sees the new worker instance immediately.
|
|
271
|
+
*/
|
|
272
|
+
this.workerPromise = new DeferredPromise((resolve) => {
|
|
273
|
+
resolve([worker, registration])
|
|
274
|
+
})
|
|
275
|
+
}
|
|
185
276
|
|
|
186
277
|
this.#channel.on('REQUEST', this.#handleRequest.bind(this))
|
|
187
278
|
this.#channel.on('RESPONSE', this.#handleResponse.bind(this))
|
|
188
279
|
|
|
189
|
-
window.addEventListener(
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
280
|
+
window.addEventListener(
|
|
281
|
+
'beforeunload',
|
|
282
|
+
() => {
|
|
283
|
+
if (worker.state !== 'redundant') {
|
|
284
|
+
this.#channel.postMessage('CLIENT_CLOSED')
|
|
285
|
+
}
|
|
193
286
|
|
|
194
|
-
|
|
287
|
+
clearInterval(this.#keepAliveInterval)
|
|
195
288
|
|
|
196
|
-
|
|
197
|
-
|
|
289
|
+
window.postMessage({ type: 'msw/worker:stop' })
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
signal: this.#listenerController?.signal,
|
|
293
|
+
},
|
|
294
|
+
)
|
|
198
295
|
|
|
199
296
|
await this.#checkWorkerIntegrity().catch((error) => {
|
|
200
297
|
devUtils.error(
|
|
@@ -207,7 +304,7 @@ Please consider using a custom "serviceWorker.url" option to point to the actual
|
|
|
207
304
|
this.#channel.postMessage('KEEPALIVE_REQUEST')
|
|
208
305
|
}, 5000)
|
|
209
306
|
|
|
210
|
-
if (!this
|
|
307
|
+
if (!this.#options.quiet) {
|
|
211
308
|
validateWorkerScope(registration)
|
|
212
309
|
}
|
|
213
310
|
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { type ServiceWorkerSourceOptions } from '../sources/service-worker-source'
|
|
2
|
+
import { shouldInvalidateWorker } from './should-invalidate-worker'
|
|
3
|
+
|
|
4
|
+
function createOptions(
|
|
5
|
+
overrides: Partial<ServiceWorkerSourceOptions> = {},
|
|
6
|
+
): ServiceWorkerSourceOptions {
|
|
7
|
+
return {
|
|
8
|
+
serviceWorker: {
|
|
9
|
+
url: '/mockServiceWorker.js',
|
|
10
|
+
options: { scope: '/' },
|
|
11
|
+
},
|
|
12
|
+
...overrides,
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
it('returns true when the worker url differs', () => {
|
|
17
|
+
expect(
|
|
18
|
+
shouldInvalidateWorker(
|
|
19
|
+
createOptions({
|
|
20
|
+
serviceWorker: { url: '/a.js', options: { scope: '/' } },
|
|
21
|
+
}),
|
|
22
|
+
createOptions({
|
|
23
|
+
serviceWorker: { url: '/b.js', options: { scope: '/' } },
|
|
24
|
+
}),
|
|
25
|
+
),
|
|
26
|
+
).toBe(true)
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
it('returns true when the registration options differ', () => {
|
|
30
|
+
expect(
|
|
31
|
+
shouldInvalidateWorker(
|
|
32
|
+
createOptions({
|
|
33
|
+
serviceWorker: { url: '/sw.js', options: { scope: '/' } },
|
|
34
|
+
}),
|
|
35
|
+
createOptions({
|
|
36
|
+
serviceWorker: { url: '/sw.js', options: { scope: '/app' } },
|
|
37
|
+
}),
|
|
38
|
+
),
|
|
39
|
+
).toBe(true)
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
it('returns true when only one side has registration options', () => {
|
|
43
|
+
expect(
|
|
44
|
+
shouldInvalidateWorker(
|
|
45
|
+
createOptions({ serviceWorker: { url: '/sw.js' } }),
|
|
46
|
+
createOptions({
|
|
47
|
+
serviceWorker: { url: '/sw.js', options: { scope: '/' } },
|
|
48
|
+
}),
|
|
49
|
+
),
|
|
50
|
+
).toBe(true)
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('returns true when findWorker differs by reference', () => {
|
|
54
|
+
expect(
|
|
55
|
+
shouldInvalidateWorker(
|
|
56
|
+
createOptions({ findWorker: () => true }),
|
|
57
|
+
createOptions({ findWorker: () => true }),
|
|
58
|
+
),
|
|
59
|
+
).toBe(true)
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
it('returns true when findWorker is added on one side', () => {
|
|
63
|
+
expect(
|
|
64
|
+
shouldInvalidateWorker(
|
|
65
|
+
createOptions(),
|
|
66
|
+
createOptions({ findWorker: () => true }),
|
|
67
|
+
),
|
|
68
|
+
).toBe(true)
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
it('returns false for the same options reference', () => {
|
|
72
|
+
const options = createOptions()
|
|
73
|
+
expect(shouldInvalidateWorker(options, options)).toBe(false)
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
it('returns false for deeply equal options', () => {
|
|
77
|
+
expect(
|
|
78
|
+
shouldInvalidateWorker(
|
|
79
|
+
createOptions({
|
|
80
|
+
serviceWorker: { url: '/sw.js', options: { scope: '/' } },
|
|
81
|
+
}),
|
|
82
|
+
createOptions({
|
|
83
|
+
serviceWorker: { url: '/sw.js', options: { scope: '/' } },
|
|
84
|
+
}),
|
|
85
|
+
),
|
|
86
|
+
).toBe(false)
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
it('returns false for the same worker url without options', () => {
|
|
90
|
+
expect(
|
|
91
|
+
shouldInvalidateWorker(
|
|
92
|
+
createOptions({ serviceWorker: { url: '/sw.js' } }),
|
|
93
|
+
createOptions({ serviceWorker: { url: '/sw.js' } }),
|
|
94
|
+
),
|
|
95
|
+
).toBe(false)
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
it('returns false when findWorker is the same reference', () => {
|
|
99
|
+
const findWorker = () => true
|
|
100
|
+
expect(
|
|
101
|
+
shouldInvalidateWorker(
|
|
102
|
+
createOptions({ findWorker }),
|
|
103
|
+
createOptions({ findWorker }),
|
|
104
|
+
),
|
|
105
|
+
).toBe(false)
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
it('returns false regardless of the "quiet" option', () => {
|
|
109
|
+
expect(
|
|
110
|
+
shouldInvalidateWorker(
|
|
111
|
+
createOptions({ quiet: true }),
|
|
112
|
+
createOptions({ quiet: true }),
|
|
113
|
+
),
|
|
114
|
+
).toBe(false)
|
|
115
|
+
|
|
116
|
+
expect(
|
|
117
|
+
shouldInvalidateWorker(
|
|
118
|
+
createOptions({ quiet: false }),
|
|
119
|
+
createOptions({ quiet: true }),
|
|
120
|
+
),
|
|
121
|
+
).toBe(false)
|
|
122
|
+
})
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type ServiceWorkerSourceOptions } from '../sources/service-worker-source'
|
|
2
|
+
|
|
3
|
+
export function shouldInvalidateWorker(
|
|
4
|
+
prevOptions: ServiceWorkerSourceOptions,
|
|
5
|
+
nextOptions: ServiceWorkerSourceOptions,
|
|
6
|
+
): boolean {
|
|
7
|
+
return (
|
|
8
|
+
prevOptions.findWorker !== nextOptions.findWorker ||
|
|
9
|
+
prevOptions.serviceWorker.url !== nextOptions.serviceWorker.url ||
|
|
10
|
+
JSON.stringify(prevOptions.serviceWorker.options) !==
|
|
11
|
+
JSON.stringify(nextOptions.serviceWorker.options)
|
|
12
|
+
)
|
|
13
|
+
}
|
|
@@ -4,10 +4,6 @@ import { isObject } from '#core/utils/internal/isObject'
|
|
|
4
4
|
import type { StringifiedResponse } from '../glossary'
|
|
5
5
|
import { supportsServiceWorker } from '../utils/supports'
|
|
6
6
|
|
|
7
|
-
export interface WorkerChannelOptions {
|
|
8
|
-
worker: Promise<ServiceWorker>
|
|
9
|
-
}
|
|
10
|
-
|
|
11
7
|
export type WorkerChannelEventMap = {
|
|
12
8
|
REQUEST: WorkerEvent<IncomingWorkerRequest>
|
|
13
9
|
RESPONSE: WorkerEvent<IncomingWorkerResponse>
|
|
@@ -121,25 +117,42 @@ type OutgoingWorkerEvents =
|
|
|
121
117
|
| 'KEEPALIVE_REQUEST'
|
|
122
118
|
| 'CLIENT_CLOSED'
|
|
123
119
|
|
|
124
|
-
export
|
|
125
|
-
|
|
126
|
-
|
|
120
|
+
export interface WorkerChannelOptions {
|
|
121
|
+
getWorker: () => Promise<ServiceWorker>
|
|
122
|
+
}
|
|
127
123
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
124
|
+
export class WorkerChannel extends Emitter<WorkerChannelEventMap> {
|
|
125
|
+
#getWorker: WorkerChannelOptions['getWorker']
|
|
126
|
+
#controller: AbortController
|
|
131
127
|
|
|
132
|
-
|
|
133
|
-
|
|
128
|
+
constructor(options: WorkerChannelOptions) {
|
|
129
|
+
super()
|
|
134
130
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
131
|
+
invariant(
|
|
132
|
+
SUPPORTS_SERVICE_WORKER,
|
|
133
|
+
'Failed to open a WorkerChannel: Service Worker is not supported in this environment.',
|
|
134
|
+
)
|
|
138
135
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
136
|
+
this.#getWorker = options.getWorker
|
|
137
|
+
this.#controller = new AbortController()
|
|
138
|
+
|
|
139
|
+
navigator.serviceWorker.addEventListener(
|
|
140
|
+
'message',
|
|
141
|
+
async (event) => {
|
|
142
|
+
const worker = await this.#getWorker()
|
|
143
|
+
|
|
144
|
+
if (event.source != null && event.source !== worker) {
|
|
145
|
+
return
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (event.data && isObject(event.data) && 'type' in event.data) {
|
|
149
|
+
this.emit(new WorkerEvent<any, any, any>(event))
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
signal: this.#controller.signal,
|
|
154
|
+
},
|
|
155
|
+
)
|
|
143
156
|
}
|
|
144
157
|
|
|
145
158
|
/**
|
|
@@ -149,11 +162,20 @@ export class WorkerChannel extends Emitter<WorkerChannelEventMap> {
|
|
|
149
162
|
public postMessage(type: OutgoingWorkerEvents): void {
|
|
150
163
|
invariant(
|
|
151
164
|
SUPPORTS_SERVICE_WORKER,
|
|
152
|
-
'Failed to post message on a WorkerChannel: the Service Worker API is unavailable in this
|
|
165
|
+
'Failed to post message on a WorkerChannel: the Service Worker API is unavailable in this environment. This is likely an issue with MSW. Please report it on GitHub: https://github.com/mswjs/msw/issues',
|
|
153
166
|
)
|
|
154
167
|
|
|
155
|
-
this.
|
|
168
|
+
this.#getWorker().then((worker) => {
|
|
156
169
|
worker.postMessage(type)
|
|
157
170
|
})
|
|
158
171
|
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Terminal teardown. Removes the `navigator.serviceWorker` message listener
|
|
175
|
+
* and all emitter subscriptions. The channel is not usable afterwards.
|
|
176
|
+
*/
|
|
177
|
+
public terminate(): void {
|
|
178
|
+
this.#controller.abort()
|
|
179
|
+
this.removeAllListeners()
|
|
180
|
+
}
|
|
159
181
|
}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { invariant } from 'outvariant'
|
|
2
2
|
import { Emitter, type DefaultEventMap } from 'rettime'
|
|
3
3
|
import {
|
|
4
|
-
|
|
4
|
+
NetworkSource,
|
|
5
5
|
type ExtractSourceEvents,
|
|
6
6
|
} from './sources/network-source'
|
|
7
7
|
import { type NetworkFrameResolutionContext } from './frames/network-frame'
|
|
8
8
|
import { type UnhandledFrameHandle } from './on-unhandled-frame'
|
|
9
9
|
import {
|
|
10
|
-
AnyHandler,
|
|
11
10
|
HandlersController,
|
|
12
11
|
InMemoryHandlersController,
|
|
12
|
+
type AnyHandler,
|
|
13
13
|
} from './handlers-controller'
|
|
14
14
|
import { toReadonlyArray } from '../utils/internal/toReadonlyArray'
|
|
15
15
|
|
|
@@ -161,6 +161,14 @@ export function defineNetwork<Sources extends Array<NetworkSource<any>>>(
|
|
|
161
161
|
readyState = NetworkReadyState.ENABLED
|
|
162
162
|
|
|
163
163
|
const result = resolvedOptions.sources.map((source) => {
|
|
164
|
+
/**
|
|
165
|
+
* @note Preemptively disable the network source before enabling.
|
|
166
|
+
* This intentionally calls only the prototype method that clears the
|
|
167
|
+
* event listeners and nothing else. This prevents the "frame" listeners
|
|
168
|
+
* from accumulating across enable/disable in case the source is a singleton.
|
|
169
|
+
*/
|
|
170
|
+
NetworkSource.prototype.disable.call(source)
|
|
171
|
+
|
|
164
172
|
source.on('frame', async ({ frame }) => {
|
|
165
173
|
frame.events.on('*', (event) => events.emit(event), {
|
|
166
174
|
signal: listenersController.signal,
|
|
@@ -2,7 +2,8 @@ import { http } from '../../http'
|
|
|
2
2
|
import { graphql } from '../../graphql'
|
|
3
3
|
import { ws } from '../../ws'
|
|
4
4
|
import { bypass } from '../../bypass'
|
|
5
|
-
import {
|
|
5
|
+
import type { HttpNetworkFrameEventMap } from './http-frame'
|
|
6
|
+
import { HttpNetworkFrame } from './http-frame'
|
|
6
7
|
import { InMemoryHandlersController } from '#core/experimental/handlers-controller'
|
|
7
8
|
|
|
8
9
|
beforeAll(() => {
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { TypedEvent } from 'rettime'
|
|
2
2
|
import { until } from 'until-async'
|
|
3
3
|
import { createRequestId } from '@mswjs/interceptors'
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
NetworkFrame,
|
|
6
|
+
type NetworkFrameResolutionContext,
|
|
7
|
+
} from './network-frame'
|
|
5
8
|
import { toPublicUrl } from '../../utils/request/toPublicUrl'
|
|
6
9
|
import { executeHandlers } from '../../utils/executeHandlers'
|
|
7
10
|
import { storeResponseCookies } from '../../utils/request/storeResponseCookies'
|
|
@@ -11,7 +14,8 @@ import {
|
|
|
11
14
|
executeUnhandledFrameHandle,
|
|
12
15
|
type UnhandledFrameHandle,
|
|
13
16
|
} from '../on-unhandled-frame'
|
|
14
|
-
import { HandlersController
|
|
17
|
+
import type { HandlersController } from '../handlers-controller'
|
|
18
|
+
import { type AnyHandler } from '../handlers-controller'
|
|
15
19
|
import { type RequestHandler } from '../../handlers/RequestHandler'
|
|
16
20
|
|
|
17
21
|
interface HttpNetworkFrameOptions {
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import { http } from '../../http'
|
|
2
2
|
import { graphql } from '../../graphql'
|
|
3
3
|
import { ws } from '../../ws'
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
WebSocketNetworkFrameEventMap,
|
|
7
|
-
} from './websocket-frame'
|
|
4
|
+
import type { WebSocketNetworkFrameEventMap } from './websocket-frame'
|
|
5
|
+
import { WebSocketNetworkFrame } from './websocket-frame'
|
|
8
6
|
import { createTestWebSocketConnection } from '../../../../test/support/ws-test-utils'
|
|
9
7
|
import { InMemoryHandlersController } from '#core/experimental/handlers-controller'
|
|
10
8
|
|
|
@@ -11,10 +11,11 @@ import {
|
|
|
11
11
|
} from './network-frame'
|
|
12
12
|
import {
|
|
13
13
|
executeUnhandledFrameHandle,
|
|
14
|
-
UnhandledFrameHandle,
|
|
14
|
+
type UnhandledFrameHandle,
|
|
15
15
|
} from '../on-unhandled-frame'
|
|
16
16
|
import { devUtils } from '../../utils/internal/devUtils'
|
|
17
|
-
import { HandlersController
|
|
17
|
+
import type { HandlersController } from '../handlers-controller'
|
|
18
|
+
import { type AnyHandler } from '../handlers-controller'
|
|
18
19
|
|
|
19
20
|
export interface WebSocketNetworkFrameOptions {
|
|
20
21
|
connection: WebSocketConnectionData
|
|
@@ -31,7 +31,7 @@ export abstract class HandlersController {
|
|
|
31
31
|
invariant(
|
|
32
32
|
this.#validateHandlers(initialHandlers),
|
|
33
33
|
devUtils.formatMessage(
|
|
34
|
-
'
|
|
34
|
+
'Failed to apply given request handlers: invalid input. Did you forget to spread the request handlers Array?',
|
|
35
35
|
),
|
|
36
36
|
)
|
|
37
37
|
|
|
@@ -5,10 +5,8 @@ import type {
|
|
|
5
5
|
} from '@mswjs/interceptors/WebSocket'
|
|
6
6
|
import { HttpNetworkFrame } from './frames/http-frame'
|
|
7
7
|
import { WebSocketNetworkFrame } from './frames/websocket-frame'
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
UnhandledFrameCallback,
|
|
11
|
-
} from './on-unhandled-frame'
|
|
8
|
+
import type { UnhandledFrameCallback } from './on-unhandled-frame'
|
|
9
|
+
import { executeUnhandledFrameHandle } from './on-unhandled-frame'
|
|
12
10
|
|
|
13
11
|
beforeAll(() => {
|
|
14
12
|
vi.spyOn(console, 'warn').mockImplementation(() => void 0)
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { type DefaultEventMap, Emitter } from 'rettime'
|
|
2
|
-
import { LifeCycleEventEmitter } from '../sharedOptions'
|
|
2
|
+
import { type LifeCycleEventEmitter } from '../sharedOptions'
|
|
3
|
+
import type { HandlersController } from './handlers-controller'
|
|
3
4
|
import {
|
|
4
|
-
AnyHandler,
|
|
5
|
-
HandlersController,
|
|
6
5
|
InMemoryHandlersController,
|
|
6
|
+
type AnyHandler,
|
|
7
7
|
} from './handlers-controller'
|
|
8
8
|
import { Disposable } from '../utils/internal/Disposable'
|
|
9
9
|
import { toReadonlyArray } from '../utils/internal/toReadonlyArray'
|
|
@@ -1,9 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
Interceptor,
|
|
4
|
-
RequestController,
|
|
5
|
-
type HttpRequestEventMap,
|
|
6
|
-
} from '@mswjs/interceptors'
|
|
1
|
+
import type { Interceptor, RequestController } from '@mswjs/interceptors'
|
|
2
|
+
import { BatchInterceptor, type HttpRequestEventMap } from '@mswjs/interceptors'
|
|
7
3
|
import type {
|
|
8
4
|
WebSocketConnectionData,
|
|
9
5
|
WebSocketEventMap,
|