|
3 | 3 | // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
|
5 | 5 | import 'dart:async'; |
| 6 | +import 'dart:convert'; |
6 | 7 |
|
| 8 | +import 'package:dwds/data/debug_event.dart'; |
7 | 9 | import 'package:dwds/data/hot_reload_request.dart'; |
8 | 10 | import 'package:dwds/data/hot_reload_response.dart'; |
| 11 | +import 'package:dwds/data/register_event.dart'; |
9 | 12 | import 'package:dwds/data/service_extension_request.dart'; |
10 | 13 | import 'package:dwds/data/service_extension_response.dart'; |
11 | 14 | import 'package:dwds/src/connections/app_connection.dart'; |
@@ -126,18 +129,20 @@ class WebSocketProxyService implements VmServiceInterface { |
126 | 129 | if (!_initializedCompleter.isCompleted) _initializedCompleter.complete(); |
127 | 130 |
|
128 | 131 | // Set up appConnection.onStart listener (like Chrome flow does) |
129 | | - appConn.onStart.then((_) { |
130 | | - // Unlike Chrome flow, we don't have debugger.resumeFromStart(), but we can trigger resume |
131 | | - if (pauseIsolatesOnStart && !_hasResumed) { |
132 | | - final resumeEvent = vm_service.Event( |
133 | | - kind: vm_service.EventKind.kResume, |
134 | | - timestamp: DateTime.now().millisecondsSinceEpoch, |
135 | | - isolate: isolateRef, |
136 | | - ); |
137 | | - _hasResumed = true; |
138 | | - _streamNotify(vm_service.EventStreams.kDebug, resumeEvent); |
139 | | - } |
140 | | - }); |
| 132 | + safeUnawaited( |
| 133 | + appConn.onStart.then((_) { |
| 134 | + // Unlike Chrome flow, we don't have debugger.resumeFromStart(), but we can trigger resume |
| 135 | + if (pauseIsolatesOnStart && !_hasResumed) { |
| 136 | + final resumeEvent = vm_service.Event( |
| 137 | + kind: vm_service.EventKind.kResume, |
| 138 | + timestamp: DateTime.now().millisecondsSinceEpoch, |
| 139 | + isolate: isolateRef, |
| 140 | + ); |
| 141 | + _hasResumed = true; |
| 142 | + _streamNotify(vm_service.EventStreams.kDebug, resumeEvent); |
| 143 | + } |
| 144 | + }), |
| 145 | + ); |
141 | 146 |
|
142 | 147 | // Send pause event if enabled |
143 | 148 | if (pauseIsolatesOnStart) { |
@@ -523,6 +528,63 @@ class WebSocketProxyService implements VmServiceInterface { |
523 | 528 | } |
524 | 529 | } |
525 | 530 |
|
| 531 | + /// Parses the [RegisterEvent] and emits a corresponding Dart VM Service |
| 532 | + /// protocol [Event]. |
| 533 | + void parseRegisterEvent(RegisterEvent registerEvent) { |
| 534 | + _logger.fine('Parsing RegisterEvent: ${registerEvent.eventData}'); |
| 535 | + |
| 536 | + if (!_isIsolateRunning || _isolateRef == null) { |
| 537 | + _logger.warning('Cannot register service extension - no isolate running'); |
| 538 | + return; |
| 539 | + } |
| 540 | + |
| 541 | + final service = registerEvent.eventData; |
| 542 | + |
| 543 | + // Add the service to the isolate's extension RPCs if we had access to the isolate |
| 544 | + // In WebSocket mode, we don't maintain the full isolate object like Chrome mode, |
| 545 | + // but we can still emit the ServiceExtensionAdded event for tooling |
| 546 | + |
| 547 | + final event = vm_service.Event( |
| 548 | + kind: vm_service.EventKind.kServiceExtensionAdded, |
| 549 | + timestamp: DateTime.now().millisecondsSinceEpoch, |
| 550 | + isolate: _isolateRef!, |
| 551 | + ); |
| 552 | + event.extensionRPC = service; |
| 553 | + |
| 554 | + _streamNotify(vm_service.EventStreams.kIsolate, event); |
| 555 | + _logger.fine('Emitted ServiceExtensionAdded event for: $service'); |
| 556 | + } |
| 557 | + |
| 558 | + /// Parses the [BatchedDebugEvents] and emits corresponding Dart VM Service |
| 559 | + /// protocol [Event]s. |
| 560 | + void parseBatchedDebugEvents(BatchedDebugEvents debugEvents) { |
| 561 | + for (final debugEvent in debugEvents.events) { |
| 562 | + parseDebugEvent(debugEvent); |
| 563 | + } |
| 564 | + } |
| 565 | + |
| 566 | + /// Parses the [DebugEvent] and emits a corresponding Dart VM Service |
| 567 | + /// protocol [Event]. |
| 568 | + void parseDebugEvent(DebugEvent debugEvent) { |
| 569 | + if (!_isIsolateRunning || _isolateRef == null) { |
| 570 | + _logger.warning('Cannot parse debug event - no isolate running'); |
| 571 | + return; |
| 572 | + } |
| 573 | + |
| 574 | + _streamNotify( |
| 575 | + vm_service.EventStreams.kExtension, |
| 576 | + vm_service.Event( |
| 577 | + kind: vm_service.EventKind.kExtension, |
| 578 | + timestamp: DateTime.now().millisecondsSinceEpoch, |
| 579 | + isolate: _isolateRef!, |
| 580 | + ) |
| 581 | + ..extensionKind = debugEvent.kind |
| 582 | + ..extensionData = vm_service.ExtensionData.parse( |
| 583 | + jsonDecode(debugEvent.eventData) as Map<String, dynamic>, |
| 584 | + ), |
| 585 | + ); |
| 586 | + } |
| 587 | + |
526 | 588 | @override |
527 | 589 | Future<Success> setFlag(String name, String value) => |
528 | 590 | wrapInErrorHandlerAsync('setFlag', () => _setFlag(name, value)); |
@@ -608,7 +670,7 @@ class WebSocketProxyService implements VmServiceInterface { |
608 | 670 | if (!_hasResumed && _currentPauseEvent != null) { |
609 | 671 | appConnection.runMain(); |
610 | 672 | } |
611 | | - |
| 673 | + |
612 | 674 | // Prevent multiple resume calls |
613 | 675 | if (_hasResumed && _currentPauseEvent == null) { |
614 | 676 | return Success(); |
|
0 commit comments