connect method

Future<void> connect()

Implementation

Future<void> connect() async {
  if (_state is! Disconnected) {
    SdkLogger.i('Already connected or connecting');
    return;
  }
  _shouldReconnect = true;
  _updateState(const Connecting());

  try {
    final protocol = ssl ? 'wss' : 'ws';
    var uri = Uri.parse('$protocol://$host/v1/database/$database/subscribe');

    final headers = <String, dynamic>{};

    final queryParams = <String, String>{};
    if (!brotliNativelySupported) {
      // Server default is Brotli; pure-Dart `package:brotli` miscompiles on
      // dart2js; older browsers lack DecompressionStream('br') (Chrome 138+
      // / Firefox 142+). Ask server to skip compression when we can't
      // decompress Brotli ourselves.
      queryParams['compression'] = 'None';
    }
    if (kIsWeb && _currentToken != null) {
      final wsToken = await _getWebSocketToken();
      if (wsToken != null) {
        queryParams['token'] = wsToken;
      }
    } else if (_currentToken != null) {
      headers['Authorization'] = 'Bearer $_currentToken';
    }
    if (queryParams.isNotEmpty) {
      uri = uri.replace(queryParameters: queryParams);
    }

    _channel = _socketFactory(
      uri,
      kPreferredWsProtocols,
      headers,
      connectTimeout: config.connectTimeout,
      // On VM, IOWebSocketChannel uses this to send WS-level Ping
      // frames. On web it's ignored (HtmlWebSocketChannel doesn't
      // expose WS ping/pong to JS).
      pingInterval: kIsWeb ? null : config.pingInterval,
    );
    await _channel!.ready;
    _negotiatedProtocol = normalizeWsProtocol(_channel!.protocol);
    SdkLogger.i('Negotiated WebSocket protocol: ${_negotiatedProtocol.name}');
    _setupMessageListener();
    // On VM the WS-level ping (wired above) handles dead-socket
    // detection natively. On web we still need the app-layer
    // KeepAliveMonitor because browsers don't expose WS ping.
    if (kIsWeb) {
      _setupKeepAlive();
    }
    _updateState(const Connected());
    _reconnectAttempts = 0;
    _updateQuality();
  } catch (e) {
    SdkLogger.e('Connection failed: $e');
    _updateState(const Disconnected());
    _channel = null;

    final errorString = e.toString();
    if (errorString.contains('401') || errorString.contains('Unauthorized')) {
      throw SpacetimeDbAuthException(
        'Authentication failed (401). Token may be invalid or expired.',
      );
    }

    throw SpacetimeDbConnectionException(
      'Connection failed: $e',
      lastKnownState: const Disconnected(),
    );
  }
}