11pub ( crate ) mod module_bindings;
22
3+ #[ cfg( all( target_arch = "wasm32" , feature = "web" ) ) ]
4+ use std:: sync:: OnceLock ;
5+
36use module_bindings:: * ;
47
58use spacetimedb_sdk:: { DbConnectionBuilder , DbContext , Table } ;
@@ -131,11 +134,29 @@ pub(crate) async fn dispatch() {
131134 . subscribe ( "SELECT * FROM disconnected" ) ;
132135
133136 #[ cfg( not( target_arch = "wasm32" ) ) ]
134- new_connection. run_threaded ( ) ;
137+ let reconnect_join_handle = new_connection. run_threaded ( ) ;
135138 #[ cfg( target_arch = "wasm32" ) ]
136139 new_connection. run_background_task ( ) ;
137140
138- reconnect_test_counter. wait_for_all ( ) ;
141+ wait_for_all ( & reconnect_test_counter) . await ;
142+
143+ // The second connection has no disconnect assertion, but it still needs an explicit
144+ // shutdown on wasm so the background task releases its websocket before the test exits.
145+ disconnect_connection ( & new_connection) . await ;
146+ #[ cfg( not( target_arch = "wasm32" ) ) ]
147+ reconnect_join_handle. join ( ) . unwrap ( ) ;
148+ }
149+
150+ async fn wait_for_all ( test_counter : & std:: sync:: Arc < TestCounter > ) {
151+ #[ cfg( target_arch = "wasm32" ) ]
152+ {
153+ // wasm/web callbacks run on the JS event loop, so this wait must stay async.
154+ test_counter. wait_for_all_async ( ) . await ;
155+ return ;
156+ }
157+
158+ #[ cfg( not( target_arch = "wasm32" ) ) ]
159+ test_counter. wait_for_all ( ) ;
139160}
140161
141162#[ cfg( not( target_arch = "wasm32" ) ) ]
@@ -144,6 +165,19 @@ async fn build_connection(builder: DbConnectionBuilder<RemoteModule>) -> DbConne
144165}
145166
146167#[ cfg( target_arch = "wasm32" ) ]
147- fn build_connection ( builder : DbConnectionBuilder < RemoteModule > ) -> DbConnection {
148- futures:: executor:: block_on ( builder. build ( ) ) . unwrap ( )
168+ async fn build_connection ( builder : DbConnectionBuilder < RemoteModule > ) -> DbConnection {
169+ // Web builds use async connection setup, so awaiting here avoids blocking the event loop
170+ // before websocket callbacks have a chance to run.
171+ builder. build ( ) . await . unwrap ( )
172+ }
173+
174+ async fn disconnect_connection ( connection : & DbConnection ) {
175+ connection. disconnect ( ) . unwrap ( ) ;
176+
177+ #[ cfg( target_arch = "wasm32" ) ]
178+ {
179+ // Yield once so the queued disconnect mutation is processed by the background task
180+ // before the wasm test function returns to Node.
181+ gloo_timers:: future:: TimeoutFuture :: new ( 0 ) . await ;
182+ }
149183}
0 commit comments