diff --git a/dwds/CHANGELOG.md b/dwds/CHANGELOG.md index 9bfd99e19..860c51bf8 100644 --- a/dwds/CHANGELOG.md +++ b/dwds/CHANGELOG.md @@ -1,5 +1,6 @@ -## 26.1.0-wip +## 26.1.0 +- `reloadSources` and `hotRestart` now throw an RPC error with `kServerError` code when `NoClientsAvailableException` is caught (no browser clients are connected), allowing tooling to detect and handle this scenario. - `pause` now does not send a `PauseInterrupted` event in `WebSocketProxyService` as we didn't actually pause. diff --git a/dwds/lib/src/services/web_socket_proxy_service.dart b/dwds/lib/src/services/web_socket_proxy_service.dart index cb97514f3..c9eefc80b 100644 --- a/dwds/lib/src/services/web_socket_proxy_service.dart +++ b/dwds/lib/src/services/web_socket_proxy_service.dart @@ -33,6 +33,9 @@ const _pauseIsolatesOnStartFlag = 'pause_isolates_on_start'; /// closes before the new connection is established, preventing premature isolate destruction. const _isolateDestructionGracePeriod = Duration(seconds: 15); +/// Error message when no clients are available for hot reload/restart. +const kNoClientsAvailable = 'No clients available.'; + /// Tracks hot reload responses from multiple browser windows/tabs. class _HotReloadTracker { final String requestId; @@ -117,6 +120,16 @@ class _ServiceExtensionTracker { } } +/// Exception thrown when no browser clients are connected to DWDS. +class NoClientsAvailableException implements Exception { + final String message; + + NoClientsAvailableException._(this.message); + + @override + String toString() => 'NoClientsAvailableException: $message'; +} + /// WebSocket-based VM service proxy for web debugging. class WebSocketProxyService extends ProxyService { final _logger = Logger('WebSocketProxyService'); @@ -504,6 +517,14 @@ class WebSocketProxyService extends ProxyService { await _performWebSocketHotReload(); _logger.info('Hot reload completed successfully'); return _ReloadReportWithMetadata(success: true); + } on NoClientsAvailableException catch (e) { + // Throw RPC error with kIsolateCannotReload code when no browser clients are + // connected. + throw vm_service.RPCError( + 'reloadSources', + vm_service.RPCErrorKind.kIsolateCannotReload.code, + 'Hot reload failed: ${e.message}', + ); } catch (e) { _logger.warning('Hot reload failed: $e'); return _ReloadReportWithMetadata(success: false, notices: [e.toString()]); @@ -518,6 +539,14 @@ class WebSocketProxyService extends ProxyService { await _performWebSocketHotRestart(); _logger.info('Hot restart completed successfully'); return {'result': vm_service.Success().toJson()}; + } on NoClientsAvailableException catch (e) { + // Throw RPC error with kIsolateCannotReload code when no browser clients are + // connected. + throw vm_service.RPCError( + 'hotRestart', + vm_service.RPCErrorKind.kIsolateCannotReload.code, + 'Hot restart failed: ${e.message}', + ); } catch (e) { _logger.warning('Hot restart failed: $e'); return { @@ -611,7 +640,8 @@ class WebSocketProxyService extends ProxyService { }); if (clientCount == 0) { - throw StateError('No clients available for hot reload'); + _logger.warning(kNoClientsAvailable); + throw NoClientsAvailableException._(kNoClientsAvailable); } // Create tracker for this hot reload request @@ -671,7 +701,8 @@ class WebSocketProxyService extends ProxyService { }); if (clientCount == 0) { - throw StateError('No clients available for hot restart'); + _logger.warning(kNoClientsAvailable); + throw NoClientsAvailableException._(kNoClientsAvailable); } // Create tracker for this hot restart request @@ -737,9 +768,7 @@ class WebSocketProxyService extends ProxyService { final request = ServiceExtensionRequest.fromArgs( id: requestId, method: method, - args: args != null - ? Map.from(args) - : {}, + args: {...?args}, ); // Send the request and get the number of connected clients @@ -940,8 +969,8 @@ class WebSocketProxyService extends ProxyService { /// Pauses execution of the isolate. @override Future pause(String isolateId) => - // Can't pause with the web socket implementation, so do nothing. - Future.value(Success()); + // Can't pause with the web socket implementation, so do nothing. + Future.value(Success()); /// Resumes execution of the isolate. @override diff --git a/dwds/lib/src/version.dart b/dwds/lib/src/version.dart index 0b0c9bb3b..c1361a8f5 100644 --- a/dwds/lib/src/version.dart +++ b/dwds/lib/src/version.dart @@ -1,2 +1,2 @@ // Generated code. Do not modify. -const packageVersion = '26.1.0-wip'; +const packageVersion = '26.1.0'; diff --git a/dwds/pubspec.yaml b/dwds/pubspec.yaml index 53b399218..19616af0a 100644 --- a/dwds/pubspec.yaml +++ b/dwds/pubspec.yaml @@ -1,6 +1,6 @@ name: dwds # Every time this changes you need to run `dart run build_runner build`. -version: 26.1.0-wip +version: 26.1.0 description: >- A service that proxies between the Chrome debug protocol and the Dart VM