diff --git a/package-lock.json b/package-lock.json index 50f492b4..4f8d9d58 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@replit/river", - "version": "0.207.0", + "version": "0.207.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@replit/river", - "version": "0.207.0", + "version": "0.207.1", "license": "MIT", "dependencies": { "@msgpack/msgpack": "^3.0.0-beta2", diff --git a/package.json b/package.json index 1f47db2c..17dba475 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@replit/river", "description": "It's like tRPC but... with JSON Schema Support, duplex streaming and support for service multiplexing. Transport agnostic!", - "version": "0.207.0", + "version": "0.207.1", "type": "module", "exports": { ".": { diff --git a/transport/sessionStateMachine/SessionConnected.ts b/transport/sessionStateMachine/SessionConnected.ts index f224585f..60fc90e8 100644 --- a/transport/sessionStateMachine/SessionConnected.ts +++ b/transport/sessionStateMachine/SessionConnected.ts @@ -226,7 +226,13 @@ export class SessionConnected< // if we are not actively heartbeating, we are in passive // heartbeat mode and should send a response to the ack if (!this.isActivelyHeartbeating) { - this.sendHeartbeat(); + // purposefully make this async to avoid weird browser behavior + // where _some_ browsers will decide that it is ok to interrupt fully + // synchronous code execution (e.g. an existing .send) to receive a + // websocket message and hit this codepath + void Promise.resolve().then(() => { + this.sendHeartbeat(); + }); } }; diff --git a/transport/sessionStateMachine/common.ts b/transport/sessionStateMachine/common.ts index 8a53d981..cfbbebc8 100644 --- a/transport/sessionStateMachine/common.ts +++ b/transport/sessionStateMachine/common.ts @@ -281,6 +281,10 @@ export abstract class IdentifiedSession extends CommonSession { constructMsg( partialMsg: PartialTransportMessage, ): TransportMessage { + if (this._isConsumed) { + throw new Error(ERR_CONSUMED); + } + const msg = { ...partialMsg, id: generateId(), diff --git a/transport/sessionStateMachine/stateMachine.test.ts b/transport/sessionStateMachine/stateMachine.test.ts index 1792d000..894f41ab 100644 --- a/transport/sessionStateMachine/stateMachine.test.ts +++ b/transport/sessionStateMachine/stateMachine.test.ts @@ -1958,7 +1958,9 @@ describe('session state machine', () => { ); // make sure the session acks the heartbeat - expect(conn.send).toHaveBeenCalledTimes(1); + await waitFor(() => { + expect(conn.send).toHaveBeenCalledTimes(1); + }); }); test('does not dispatch acks', async () => {