11import { Emitter } from 'strict-event-emitter'
2+ import { createRequestId } from '@mswjs/interceptors'
23import type { WebSocketConnectionData } from '@mswjs/interceptors/WebSocket'
34import {
45 type Match ,
@@ -23,13 +24,18 @@ interface WebSocketHandlerConnection extends WebSocketConnectionData {
2324export const kEmitter = Symbol ( 'kEmitter' )
2425export const kDispatchEvent = Symbol ( 'kDispatchEvent' )
2526export const kSender = Symbol ( 'kSender' )
27+ const kStopPropagationPatched = Symbol ( 'kStopPropagationPatched' )
28+ const KOnStopPropagation = Symbol ( 'KOnStopPropagation' )
2629
2730export class WebSocketHandler {
31+ public id : string
2832 public callFrame ?: string
2933
3034 protected [ kEmitter ] : Emitter < WebSocketHandlerEventMap >
3135
3236 constructor ( private readonly url : Path ) {
37+ this . id = createRequestId ( )
38+
3339 this [ kEmitter ] = new Emitter ( )
3440 this . callFrame = getCallFrame ( new Error ( ) )
3541 }
@@ -63,8 +69,74 @@ export class WebSocketHandler {
6369 params : parsedResult . match . params || { } ,
6470 }
6571
72+ // Support `event.stopPropagation()` for various client/server events.
73+ connection . client . addEventListener (
74+ 'message' ,
75+ createStopPropagationListener ( this ) ,
76+ )
77+ connection . client . addEventListener (
78+ 'close' ,
79+ createStopPropagationListener ( this ) ,
80+ )
81+
82+ connection . server . addEventListener (
83+ 'open' ,
84+ createStopPropagationListener ( this ) ,
85+ )
86+ connection . server . addEventListener (
87+ 'message' ,
88+ createStopPropagationListener ( this ) ,
89+ )
90+ connection . server . addEventListener (
91+ 'error' ,
92+ createStopPropagationListener ( this ) ,
93+ )
94+ connection . server . addEventListener (
95+ 'close' ,
96+ createStopPropagationListener ( this ) ,
97+ )
98+
6699 // Emit the connection event on the handler.
67100 // This is what the developer adds listeners for.
68101 this [ kEmitter ] . emit ( 'connection' , resolvedConnection )
69102 }
70103}
104+
105+ function createStopPropagationListener ( handler : WebSocketHandler ) {
106+ return function stopPropagationListener ( event : Event ) {
107+ const propagationStoppedAt = Reflect . get ( event , 'kPropagationStoppedAt' ) as
108+ | string
109+ | undefined
110+
111+ if ( propagationStoppedAt && handler . id !== propagationStoppedAt ) {
112+ event . stopImmediatePropagation ( )
113+ return
114+ }
115+
116+ Object . defineProperty ( event , KOnStopPropagation , {
117+ value ( this : WebSocketHandler ) {
118+ Object . defineProperty ( event , 'kPropagationStoppedAt' , {
119+ value : handler . id ,
120+ } )
121+ } ,
122+ configurable : true ,
123+ } )
124+
125+ // Since the same event instance is shared between all client/server objects,
126+ // make sure to patch its `stopPropagation` method only once.
127+ if ( ! Reflect . get ( event , kStopPropagationPatched ) ) {
128+ event . stopPropagation = new Proxy ( event . stopPropagation , {
129+ apply : ( target , thisArg , args ) => {
130+ Reflect . get ( event , KOnStopPropagation ) ?. call ( handler )
131+ return Reflect . apply ( target , thisArg , args )
132+ } ,
133+ } )
134+
135+ Object . defineProperty ( event , kStopPropagationPatched , {
136+ value : true ,
137+ // If something else attempts to redefine this, throw.
138+ configurable : false ,
139+ } )
140+ }
141+ }
142+ }
0 commit comments