@@ -9,10 +9,7 @@ import type Document from "./Document.ts";
99import { IncomingMessage } from "./IncomingMessage.ts" ;
1010import { MessageReceiver } from "./MessageReceiver.ts" ;
1111import { OutgoingMessage } from "./OutgoingMessage.ts" ;
12- import type {
13- beforeSyncPayload ,
14- onStatelessPayload ,
15- } from "./types.ts" ;
12+ import type { beforeSyncPayload , onStatelessPayload } from "./types.ts" ;
1613
1714export class Connection < Context = any > {
1815 webSocket : WebSocket ;
@@ -39,6 +36,10 @@ export class Connection<Context = any> {
3936
4037 readOnly : boolean ;
4138
39+ private messageQueue : Uint8Array [ ] = [ ] ;
40+
41+ private processingPromise : Promise < void > = Promise . resolve ( ) ;
42+
4243 /**
4344 * Constructor.
4445 */
@@ -121,6 +122,13 @@ export class Connection<Context = any> {
121122 return this ;
122123 }
123124
125+ /**
126+ * Returns a promise that resolves when all queued messages have been processed.
127+ */
128+ waitForPendingMessages ( ) : Promise < void > {
129+ return this . processingPromise ;
130+ }
131+
124132 /**
125133 * Send the given message
126134 */
@@ -204,30 +212,34 @@ export class Connection<Context = any> {
204212 * @public
205213 */
206214 public handleMessage ( data : Uint8Array ) : void {
207- const message = new IncomingMessage ( data ) ;
208- const documentName = message . readVarString ( ) ;
209-
210- if ( documentName !== this . document . name ) return ;
211-
212- message . writeVarString ( documentName ) ;
213-
214- this . callbacks
215- . beforeHandleMessage ( this , data )
216- . then ( ( ) => {
217- try {
218- new MessageReceiver ( message ) . apply ( this . document , this ) ;
219- } catch ( e : any ) {
220- console . error (
221- `closing connection ${ this . socketId } (while handling ${ documentName } ) because of exception` ,
222- e ,
223- ) ;
224- this . close ( {
225- code : "code" in e ? e . code : ResetConnection . code ,
226- reason : "reason" in e ? e . reason : ResetConnection . reason ,
227- } ) ;
228- }
229- } )
230- . catch ( ( e : any ) => {
215+ this . messageQueue . push ( data ) ;
216+
217+ if ( this . messageQueue . length === 1 ) {
218+ this . processingPromise = this . processMessages ( ) ;
219+ }
220+ }
221+
222+ private async processMessages ( ) {
223+ while ( this . messageQueue . length > 0 ) {
224+ const rawUpdate = this . messageQueue . at ( 0 ) as Uint8Array ;
225+
226+ const message = new IncomingMessage ( rawUpdate ) ;
227+ const documentName = message . readVarString ( ) ;
228+
229+ if ( documentName !== this . document . name ) {
230+ this . messageQueue . shift ( ) ;
231+ continue ;
232+ }
233+
234+ message . writeVarString ( documentName ) ;
235+
236+ try {
237+ await this . callbacks . beforeHandleMessage ( this , rawUpdate ) ;
238+ const receiver = new MessageReceiver ( message ) ;
239+
240+ await receiver . apply ( this . document , this ) ;
241+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
242+ } catch ( e : any ) {
231243 console . error (
232244 `closing connection ${ this . socketId } (while handling ${ documentName } ) because of exception` ,
233245 e ,
@@ -236,7 +248,10 @@ export class Connection<Context = any> {
236248 code : "code" in e ? e . code : ResetConnection . code ,
237249 reason : "reason" in e ? e . reason : ResetConnection . reason ,
238250 } ) ;
239- } ) ;
251+ }
252+
253+ this . messageQueue . shift ( ) ;
254+ }
240255 }
241256}
242257
0 commit comments