@@ -71,6 +71,22 @@ export interface SharedStateOptions<T> {
7171 enablePatches ?: boolean
7272}
7373
74+ /**
75+ * Upper bound on retained syncIds. Loop echoes arrive near-immediately, so a
76+ * generous window preserves de-dup while capping memory on long-lived,
77+ * frequently-mutated states (e.g. a 1s terminal poll).
78+ */
79+ const MAX_SYNC_IDS = 1000
80+
81+ function rememberSyncId ( syncIds : Set < string > , syncId : string ) : void {
82+ syncIds . add ( syncId )
83+ if ( syncIds . size > MAX_SYNC_IDS ) {
84+ const oldest = syncIds . values ( ) . next ( ) . value
85+ if ( oldest !== undefined )
86+ syncIds . delete ( oldest )
87+ }
88+ }
89+
7490export function createSharedState < T extends object > (
7591 options : SharedStateOptions < T > ,
7692) : SharedState < T > {
@@ -91,15 +107,15 @@ export function createSharedState<T extends object>(
91107 return
92108 enableImmerPatches ( )
93109 state = applyPatches ( state as unknown as Objectish , patches as unknown as Patch [ ] ) as T
94- syncIds . add ( syncId )
110+ rememberSyncId ( syncIds , syncId )
95111 events . emit ( 'updated' , state , undefined , syncId )
96112 } ,
97113 mutate : ( fn , syncId = nanoid ( ) ) => {
98114 // Avoid loop syncs
99115 if ( syncIds . has ( syncId ) )
100116 return
101117
102- syncIds . add ( syncId )
118+ rememberSyncId ( syncIds , syncId )
103119 if ( enablePatches ) {
104120 const [ newState , patches ] = produceWithPatches (
105121 state as unknown as Objectish ,
0 commit comments