Repository: trpc/trpc
File: packages/client/src/links/wsLink/wsClient/wsClient.ts (TypeScript)

Code before:
        ws.close(3000);
        handleCloseOrError(error);
      });
    });

    ws.addEventListener('message', ({ data }) => {
      this.inactivityTimeout.reset();

      if (typeof data !== 'string' || ['PING', 'PONG'].includes(data)) return;

      const incomingMessage = JSON.parse(data) as TRPCClientIncomingMessage;
      if ('method' in incomingMessage) {
        this.handleIncomingRequest(incomingMessage);
        return;
      }

      this.handleResponseMessage(incomingMessage);
    });

    ws.addEventListener('close', (event) => {
      handleCloseOrError(event);
      this.callbacks.onClose?.(event);

      if (
        !this.lazyMode ||
        (this.lazyMode && this.requestManager.hasPendingRequests())
      ) {
        this.reconnect(
          new TRPCWebSocketClosedError({
            message: 'WebSocket closed',
            cause: event,
          }),
        );
      }
    });

    ws.addEventListener('error', (event) => {
      handleCloseOrError(event);
      this.callbacks.onError?.(event);

      this.reconnect(
        new TRPCWebSocketClosedError({
          message: 'WebSocket closed',
          cause: event,
        }),
      );
    });
  }

  private handleResponseMessage(message: TRPCResponseMessage) {
    const request = this.requestManager.getPendingRequest(message.id);

Review comment (bug):
In lazy mode, I believe we wait for the next request from the client before reconnecting. You're right, though—active subscriptions are a different case. That said, I don’t think we want this same behavior for other pending requests, like queries or mutations.

```suggestion
        !this.lazyMode || /* something like: this.requestManager.hasPendingSubscriptions() */
```

At first, I thought we might want to generalize this behavior to *any* pending request, but now I’m not so sure. If that were the case, we’d probably want to write a test like this for a simple query:

**Test: A pending query triggers a reconnect**

1. The client opens a connection to the server  
2. The client sends a query, but the server doesn't respond  
3. The connection is destroyed  
4. The client reconnects and resends the same query?

But while writing this, I realized we probably *don’t* want that behavior. For queries (or mutations), if the connection drops, we generally treat the request as failed and expect the client to initiate a new request to trigger a lazy reconnect.


WDYT ?

Code after:
        ws.close(3000);
        handleCloseOrError(error);
      });
    });

    ws.addEventListener('message', ({ data }) => {
      this.inactivityTimeout.reset();

      if (typeof data !== 'string' || ['PING', 'PONG'].includes(data)) return;

      const incomingMessage = JSON.parse(data) as TRPCClientIncomingMessage;
      if ('method' in incomingMessage) {
        this.handleIncomingRequest(incomingMessage);
        return;
      }

      this.handleResponseMessage(incomingMessage);
    });

    ws.addEventListener('close', (event) => {
      handleCloseOrError(event);
      this.callbacks.onClose?.(event);

      if (!this.lazyMode || this.requestManager.hasPendingSubscriptions()) {
        this.reconnect(
          new TRPCWebSocketClosedError({
            message: 'WebSocket closed',
            cause: event,
          }),
        );
      }
    });

    ws.addEventListener('error', (event) => {
      handleCloseOrError(event);
      this.callbacks.onError?.(event);

      this.reconnect(
        new TRPCWebSocketClosedError({
          message: 'WebSocket closed',
          cause: event,
        }),
      );
    });
  }

  private handleResponseMessage(message: TRPCResponseMessage) {
    const request = this.requestManager.getPendingRequest(message.id);
    if (!request) return;

    request.callbacks.next(message);