mirror of
https://github.com/hanzoai/msw.git
synced 2026-09-24 17:00:34 +00:00
fix(defineNetwork): prevent event forwarding manually (#2740)
This commit is contained in:
committed by
GitHub
parent
0fa5c5f088
commit
50b4fd4e75
@@ -12,6 +12,7 @@ import {
|
||||
type AnyHandler,
|
||||
} from './handlers-controller'
|
||||
import { toReadonlyArray } from '../utils/internal/toReadonlyArray'
|
||||
import { Disposable } from '../utils/internal/Disposable'
|
||||
|
||||
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
|
||||
k: infer I,
|
||||
@@ -112,6 +113,7 @@ export function defineNetwork<Sources extends Array<NetworkSource<any>>>(
|
||||
): NetworkApi<Sources> {
|
||||
let readyState: NetworkReadyState = NetworkReadyState.DISABLED
|
||||
const events = new Emitter<MergeEventMaps<Sources>>()
|
||||
const disposable = new Disposable()
|
||||
|
||||
const deriveHandlersController = (
|
||||
handlers: DefineNetworkOptions<Sources>['handlers'],
|
||||
@@ -130,7 +132,6 @@ export function defineNetwork<Sources extends Array<NetworkSource<any>>>(
|
||||
* certain setup APIs, like `setupServer`, don't await `.enable` (`.listen`).
|
||||
*/
|
||||
let handlersController = deriveHandlersController(resolvedOptions.handlers)
|
||||
let listenersController: AbortController
|
||||
|
||||
return {
|
||||
get readyState() {
|
||||
@@ -138,7 +139,10 @@ export function defineNetwork<Sources extends Array<NetworkSource<any>>>(
|
||||
},
|
||||
events,
|
||||
configure(options) {
|
||||
invariant(readyState === NetworkReadyState.DISABLED, '')
|
||||
invariant(
|
||||
readyState === NetworkReadyState.DISABLED,
|
||||
'Failed to call "configure()" on the network: cannot configure an already enabled network.',
|
||||
)
|
||||
|
||||
if (
|
||||
options.handlers &&
|
||||
@@ -158,9 +162,18 @@ export function defineNetwork<Sources extends Array<NetworkSource<any>>>(
|
||||
'Failed to call "enable" on the network: already enabled',
|
||||
)
|
||||
|
||||
listenersController = new AbortController()
|
||||
readyState = NetworkReadyState.ENABLED
|
||||
|
||||
/**
|
||||
* @note Use a session object scoped to the current "enable()"
|
||||
* to prevent "frame.events" listeners from surviving across enable/disable cycles.
|
||||
* @see The note about `AbortController` below.
|
||||
*/
|
||||
const session = { active: true }
|
||||
disposable['subscriptions'].push(() => {
|
||||
session.active = false
|
||||
})
|
||||
|
||||
const result = resolvedOptions.sources.map((source) => {
|
||||
/**
|
||||
* @note Preemptively disable the network source before enabling.
|
||||
@@ -171,8 +184,19 @@ export function defineNetwork<Sources extends Array<NetworkSource<any>>>(
|
||||
NetworkSource.prototype.disable.call(source)
|
||||
|
||||
source.on('frame', async ({ frame }) => {
|
||||
frame.events.on('*', (event) => events.emit(event), {
|
||||
signal: listenersController.signal,
|
||||
frame.events.on('*', (event) => {
|
||||
/**
|
||||
* @note Prevent event forwarding manually and not via an AbortController
|
||||
* because certain runtimes, like Cloudflare, throw when referencing an
|
||||
* AbortController created in a different context. Bear in mind that the frame
|
||||
* events run in the patched request client context while the AbortController
|
||||
* is created outside, in the "defineNetwork" closure, which is a test context.
|
||||
*/
|
||||
if (!session.active) {
|
||||
return
|
||||
}
|
||||
|
||||
events.emit(event)
|
||||
})
|
||||
|
||||
const handlers = frame.getHandlers(handlersController)
|
||||
@@ -197,8 +221,8 @@ export function defineNetwork<Sources extends Array<NetworkSource<any>>>(
|
||||
'Failed to call "disable" on the network: already disabled',
|
||||
)
|
||||
|
||||
listenersController.abort()
|
||||
readyState = NetworkReadyState.DISABLED
|
||||
disposable.dispose()
|
||||
|
||||
return colorlessPromiseAll(
|
||||
resolvedOptions.sources.map((source) => source.disable()),
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { devUtils } from './devUtils'
|
||||
|
||||
export type DisposableSubscription = () => void
|
||||
|
||||
export class Disposable {
|
||||
@@ -5,8 +7,27 @@ export class Disposable {
|
||||
|
||||
public dispose() {
|
||||
let subscription: DisposableSubscription | undefined
|
||||
const errors: Array<Error> = []
|
||||
|
||||
while ((subscription = this.subscriptions.shift())) {
|
||||
subscription()
|
||||
try {
|
||||
subscription()
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
errors.push(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (errors.length > 0) {
|
||||
console.error(
|
||||
new AggregateError(
|
||||
errors,
|
||||
devUtils.formatMessage(
|
||||
'Failed to dispose of some side effects. This is likely an issue with MSW, please report it on GitHub: https://github.com/mswjs/msw/issues',
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user