@@ -128,6 +128,28 @@ vi.mock('redis', () => ({ createClient: () => makeClient() }))
128128import { FileDocStore , REDIS_AGENT_ORIGIN , REDIS_ORIGIN } from '@/handlers/file-doc-store'
129129
130130const REDIS_URL = 'redis://fake'
131+
132+ interface StoreRoomInternals {
133+ lastId : string
134+ pendingDeltas : Array < { id : string ; bytes : number } >
135+ realEdited : boolean
136+ publishes : number
137+ doc : Y . Doc
138+ seededObserved : boolean
139+ }
140+
141+ interface FileDocStoreInternals {
142+ rooms : Map < string , StoreRoomInternals >
143+ appendUpdate ( name : string , update : Uint8Array , agent ?: boolean ) : Promise < void >
144+ write : { xTrim : ( ...args : unknown [ ] ) => Promise < unknown > }
145+ maybeCompact ( name : string , force ?: boolean ) : Promise < void >
146+ }
147+
148+ /** Reaches the private state these tests assert on, without `any`. */
149+ function internals ( store : object ) : FileDocStoreInternals {
150+ return store as unknown as FileDocStoreInternals
151+ }
152+
131153const COMPACT_THRESHOLD_ENTRIES = 400
132154const NAME = 'workspace-file-doc:file-1'
133155
@@ -339,14 +361,15 @@ describe('FileDocStore', () => {
339361 const a = await newStore ( )
340362 // This task has integrated only up to entry 400 (all no-ops) — its local doc is empty and lags the
341363 // two peer entries. Inject that lagging room directly (a real edit was integrated → realEdited).
342- ; ( a as any ) . rooms . set ( NAME , {
364+ internals ( a ) . rooms . set ( NAME , {
343365 doc : new Y . Doc ( ) ,
344366 lastId : '400-0' ,
345367 publishes : 0 ,
368+ pendingDeltas : [ ] ,
346369 seededObserved : true ,
347370 realEdited : true ,
348371 } )
349- await ( a as any ) . maybeCompact ( NAME )
372+ await internals ( a ) . maybeCompact ( NAME )
350373
351374 // A fresh catch-up must still reconstruct the peer content — compaction must not have trimmed 401/402.
352375 const doc = new Y . Doc ( )
@@ -392,11 +415,11 @@ describe('FileDocStore', () => {
392415 const a = await newStore ( )
393416 const doc = new Y . Doc ( )
394417 await a . attachRoom ( NAME , doc )
395- const room = ( a as any ) . rooms . get ( NAME )
418+ const room = internals ( a ) . rooms . get ( NAME ) !
396419 expect ( room . realEdited ) . toBe ( false )
397420 // Kick off a real (non-agent) append but do NOT await it: realEdited must already be true before the
398421 // xAdd/expire awaits resolve, so any compaction racing on the awaits sees the real edit.
399- const pending = ( a as any ) . appendUpdate ( NAME , updateFor ( 'real user edit' ) )
422+ const pending = internals ( a ) . appendUpdate ( NAME , updateFor ( 'real user edit' ) )
400423 expect ( room . realEdited ) . toBe ( true )
401424 await pending
402425 doc . destroy ( )
@@ -474,16 +497,16 @@ describe('FileDocStore', () => {
474497 const a = await newStore ( )
475498 const doc = new Y . Doc ( )
476499 await a . attachRoom ( NAME , doc )
477- const room = ( a as any ) . rooms . get ( NAME )
500+ const room = internals ( a ) . rooms . get ( NAME ) !
478501 room . pendingDeltas = [ { id : '1-0' , bytes : 9 * 1024 * 1024 } ]
479502 room . realEdited = true
480503
481- const write = ( a as any ) . write
504+ const write = internals ( a ) . write
482505 const original = write . xTrim . bind ( write )
483506 write . xTrim = async ( ) => {
484507 throw new Error ( 'redis blip' )
485508 }
486- await ( a as any ) . maybeCompact ( NAME , true )
509+ await internals ( a ) . maybeCompact ( NAME , true )
487510 write . xTrim = original
488511
489512 // A failed fold must not disarm the trigger — otherwise the stream stays oversized until
@@ -496,19 +519,24 @@ describe('FileDocStore', () => {
496519 const a = await newStore ( )
497520 const doc = new Y . Doc ( )
498521 await a . attachRoom ( NAME , doc )
499- const room = ( a as any ) . rooms . get ( NAME )
522+ const room = internals ( a ) . rooms . get ( NAME ) !
500523 room . realEdited = true
501- // The tailer has integrated up to 5-0, so MINID retains 9-0. Its bytes are still in Redis,
502- // and dropping them would disarm the byte trigger while the stream kept growing.
524+ // The tailer has integrated up to 5-0, so `MINID 5-0` retains both 5-0 (the boundary is
525+ // INCLUSIVE) and 9-0. Their bytes are still in Redis, and dropping them would disarm the
526+ // byte trigger while the stream kept growing.
503527 room . lastId = '5-0'
504528 room . pendingDeltas = [
505529 { id : '3-0' , bytes : 4 * 1024 * 1024 } ,
530+ { id : '5-0' , bytes : 6 * 1024 * 1024 } ,
506531 { id : '9-0' , bytes : 7 * 1024 * 1024 } ,
507532 ]
508533
509- await ( a as any ) . maybeCompact ( NAME , true )
534+ await internals ( a ) . maybeCompact ( NAME , true )
510535
511- expect ( room . pendingDeltas ) . toEqual ( [ { id : '9-0' , bytes : 7 * 1024 * 1024 } ] )
536+ expect ( room . pendingDeltas ) . toEqual ( [
537+ { id : '5-0' , bytes : 6 * 1024 * 1024 } ,
538+ { id : '9-0' , bytes : 7 * 1024 * 1024 } ,
539+ ] )
512540 doc . destroy ( )
513541 } )
514542
@@ -525,14 +553,15 @@ describe('FileDocStore', () => {
525553 state . backing ! . seq = 400
526554
527555 const a = await newStore ( )
528- ; ( a as any ) . rooms . set ( NAME , {
556+ internals ( a ) . rooms . set ( NAME , {
529557 doc : agentDoc ,
530558 lastId : '400-0' ,
531559 publishes : 0 ,
560+ pendingDeltas : [ ] ,
532561 seededObserved : true ,
533562 realEdited : false ,
534563 } )
535- await ( a as any ) . maybeCompact ( NAME )
564+ await internals ( a ) . maybeCompact ( NAME )
536565
537566 // The snapshot must carry the AGENT marker, NOT the snapshot marker, so a peer catch-up applies it as
538567 // REDIS_AGENT_ORIGIN and never marks the doc edited — the no-persist guarantee survives compaction.
@@ -691,21 +720,23 @@ describe('FileDocStore', () => {
691720 const b = await newStore ( )
692721 const docA = new Y . Doc ( )
693722 Y . applyUpdate ( docA , peerUpdates [ 0 ] ) // A integrated up to 401
694- ; ( a as any ) . rooms . set ( NAME , {
723+ internals ( a ) . rooms . set ( NAME , {
695724 doc : docA ,
696725 lastId : '401-0' ,
697726 publishes : 0 ,
727+ pendingDeltas : [ ] ,
698728 seededObserved : true ,
699729 realEdited : true ,
700730 } )
701- ; ( b as any ) . rooms . set ( NAME , {
731+ internals ( b ) . rooms . set ( NAME , {
702732 doc : new Y . Doc ( ) ,
703733 lastId : '400-0' ,
704734 publishes : 0 ,
735+ pendingDeltas : [ ] ,
705736 seededObserved : true ,
706737 realEdited : true ,
707738 } )
708- await Promise . all ( [ ( a as any ) . maybeCompact ( NAME ) , ( b as any ) . maybeCompact ( NAME ) ] )
739+ await Promise . all ( [ internals ( a ) . maybeCompact ( NAME ) , internals ( b ) . maybeCompact ( NAME ) ] )
709740
710741 const doc = new Y . Doc ( )
711742 Y . applyUpdate ( doc , ( await a . getStreamState ( NAME ) ) ! )
0 commit comments