import { EventEmitter } from 'events' ; import { debounce } from 'mini-debounce' ; /**
 * Types of page data updates.
 *
 * add:
 *  A new page is added.
 *  The page list module will be updated.
 * update:
 *  A page is updated.
 *  The page list module will be updated if it is static data change
 *  The page data module will be updated if it is runtime data change
 * delete:
 *  A page is deleted.
 *  The page list module will be updated.
 *  Buffered update of the deleted page will be cancled.
 */ type Update = { type : 'add' | 'delete' ; pageId : string ; } | { type : 'update' ; pageId : string ; dataType : 'runtime' | 'static' ; } ; export type ScheduleUpdate = ( update : Update ) => void ; /**
 * Buffer page data updates.
 * Can flush a batch of updates together
 * and cancle unnecessary updates
 */ export class PageUpdateBuffer extends EventEmitter { /**
     * which pages should be updated
     */ private pageUpdateBuffer = new Set < string > ( ) ; /**
     * whether the page list should be updated
     */ private pageListUpdateBuffer = false ; private scheduleFlush : ( ) => void ; constructor ( ) { super ( ) ; this . scheduleFlush = debounce ( ( ) => { let havePageUpdate = false ; if ( this . pageUpdateBuffer . size > 0 ) { havePageUpdate = true ; const updates = [ ... this . pageUpdateBuffer . values ( ) ] ; this . emit ( 'page' , updates ) ; this . pageUpdateBuffer . clear ( ) ; } if ( this . pageListUpdateBuffer ) { if ( ! havePageUpdate ) this . emit ( 'page-list' ) ; this . pageListUpdateBuffer = false ; } } , 100 ) ; } scheduleUpdate ( update ) { switch ( update . type ) { case 'add' : this . pageListUpdateBuffer = true ; break ; case 'update' : if ( update . dataType === 'static' ) this . pageListUpdateBuffer = true ; else this . pageUpdateBuffer . add ( update . pageId ) ; break ; case 'delete' : this . pageListUpdateBuffer = true ; this . pageUpdateBuffer . delete ( update . pageId ) ; break ; default : throw new Error ( `invalid update type ${ JSON . stringify ( update ) }` ) ; } this . scheduleFlush ( ) ; } async batchUpdate ( exec ) { let updates = [ ] ; const _this = this ; try { await exec ( scheduleUpdate ) ; } finally { updates . forEach ( ( update ) => { _this . scheduleUpdate ( update ) ; } ) ; updates = null ; this . scheduleFlush ( ) ; } function scheduleUpdate ( update ) { if ( ! updates ) { _this . scheduleUpdate ( update ) ; return ; } updates . push ( update ) ; } } }