@@ -1379,4 +1379,141 @@ describe(`Collection.subscribeChanges`, () => {
13791379 expect ( changeEvents . length ) . toBe ( 0 )
13801380 expect ( collection . state . has ( 1 ) ) . toBe ( false )
13811381 } )
1382+
1383+ it ( `only emit a single event when a sync mutation is triggered from inside a mutation handler callback` , async ( ) => {
1384+ const callback = vi . fn ( )
1385+
1386+ interface TestItem extends Record < string , unknown > {
1387+ id : number
1388+ number : number
1389+ }
1390+
1391+ let callBegin ! : ( ) => void
1392+ let callWrite ! : ( message : Omit < ChangeMessage < TestItem > , `key`> ) => void
1393+ let callCommit ! : ( ) => void
1394+
1395+ // Create collection with pre-populated data
1396+ const collection = createCollection < TestItem > ( {
1397+ id : `test` ,
1398+ getKey : ( item ) => item . id ,
1399+ sync : {
1400+ sync : ( { begin, write, commit, markReady } ) => {
1401+ callBegin = begin
1402+ callWrite = write
1403+ callCommit = commit
1404+ // Immediately populate with initial data
1405+ begin ( )
1406+ write ( {
1407+ type : `insert` ,
1408+ value : { id : 0 , number : 15 } ,
1409+ } )
1410+ commit ( )
1411+ markReady ( )
1412+ } ,
1413+ } ,
1414+ onDelete : ( { transaction } ) => {
1415+ const { original } = transaction . mutations [ 0 ]
1416+
1417+ // IMMEDIATELY synchronously trigger the sync inside the onDelete callback promise
1418+ callBegin ( )
1419+ callWrite ( { type : `delete` , value : original } )
1420+ callCommit ( )
1421+
1422+ return Promise . resolve ( )
1423+ } ,
1424+ } )
1425+
1426+ // Subscribe to changes
1427+ const subscription = collection . subscribeChanges ( callback , {
1428+ includeInitialState : true ,
1429+ } )
1430+
1431+ callback . mockReset ( )
1432+
1433+ // Delete item 0
1434+ collection . delete ( 0 )
1435+
1436+ await new Promise ( ( resolve ) => setTimeout ( resolve , 10 ) )
1437+
1438+ expect ( callback . mock . calls . length ) . toBe ( 1 )
1439+ expect ( callback . mock . calls [ 0 ] ! [ 0 ] ) . toEqual ( [
1440+ {
1441+ type : `delete` ,
1442+ key : 0 ,
1443+ value : { id : 0 , number : 15 } ,
1444+ } ,
1445+ ] )
1446+
1447+ subscription . unsubscribe ( )
1448+ } )
1449+
1450+ it ( `only emit a single event when a sync mutation is triggered from inside a mutation handler callback after a short delay` , async ( ) => {
1451+ const callback = vi . fn ( )
1452+
1453+ interface TestItem extends Record < string , unknown > {
1454+ id : number
1455+ number : number
1456+ }
1457+
1458+ let callBegin ! : ( ) => void
1459+ let callWrite ! : ( message : Omit < ChangeMessage < TestItem > , `key`> ) => void
1460+ let callCommit ! : ( ) => void
1461+
1462+ // Create collection with pre-populated data
1463+ const collection = createCollection < TestItem > ( {
1464+ id : `test` ,
1465+ getKey : ( item ) => item . id ,
1466+ sync : {
1467+ sync : ( { begin, write, commit, markReady } ) => {
1468+ callBegin = begin
1469+ callWrite = write
1470+ callCommit = commit
1471+ // Immediately populate with initial data
1472+ begin ( )
1473+ write ( {
1474+ type : `insert` ,
1475+ value : { id : 0 , number : 15 } ,
1476+ } )
1477+ commit ( )
1478+ markReady ( )
1479+ } ,
1480+ } ,
1481+ onDelete : async ( { transaction } ) => {
1482+ const { original } = transaction . mutations [ 0 ]
1483+
1484+ // Simulate waiting for some async operation
1485+ await new Promise ( ( resolve ) => setTimeout ( resolve , 0 ) )
1486+
1487+ // Synchronously trigger the sync inside the onDelete callback promise,
1488+ // but after a short delay.
1489+ // Ordering here is important to test for a race condition!
1490+ callBegin ( )
1491+ callWrite ( { type : `delete` , value : original } )
1492+ callCommit ( )
1493+ } ,
1494+ } )
1495+
1496+ // Subscribe to changes
1497+ const subscription = collection . subscribeChanges ( callback , {
1498+ includeInitialState : true ,
1499+ } )
1500+
1501+ callback . mockReset ( )
1502+
1503+ // Delete item 0
1504+ collection . delete ( 0 )
1505+
1506+ await new Promise ( ( resolve ) => setTimeout ( resolve , 10 ) )
1507+
1508+ expect ( callback . mock . calls . length ) . toBe ( 1 )
1509+ expect ( callback . mock . calls [ 0 ] ! [ 0 ] ) . toEqual ( [
1510+ {
1511+ type : `delete` ,
1512+ key : 0 ,
1513+ value : { id : 0 , number : 15 } ,
1514+ } ,
1515+ ] )
1516+
1517+ subscription . unsubscribe ( )
1518+ } )
13821519} )
0 commit comments