import { debug as Debug } from 'debug' ; import { XAudioBuffer } from './XAudioBuffer' ; const DBG_STR = 'nf:content-cache' ; const dbg = Debug ( DBG_STR ) ; type LoadingScore = { requests : Set < Promise < XAudioBuffer > > ; graphId : string ; loaded : Promise < void > ; signalLoaded : ( ) => void ; signalFailed : ( err : Error ) => void ; } ; export class ContentCache { constructor ( private requests = new Map < string , Promise < XAudioBuffer > > ( ) , private audio = new Map < string , XAudioBuffer > ( ) , private scores = new Map < string , LoadingScore > ( ) ) { } async get ( uri , graphId , fetcher ) { const existingAudio = this . audio . get ( uri ) ; if ( existingAudio ) return existingAudio ; let lscore = this . scores . get ( graphId ) ; if ( ! lscore ) { dbg ( 'creating LoadingScore for graph %s' , graphId ) ; let signalLoaded = ( ) => { } ; let signalFailed = _ => { } ; const p = new Promise < void > ( ( resolve , reject ) => { signalLoaded = resolve ; signalFailed = reject ; } ) ; lscore = { requests : new Set ( ) , graphId , loaded : p , signalLoaded , signalFailed } ; this . scores . set ( graphId , lscore ) ; } let request ; const existingRequest = this . requests . get ( uri ) ; if ( existingRequest ) { request = existingRequest ; } else { request = fetcher ( ) ; this . requests . set ( uri , request ) ; } lscore . requests . add ( request ) ; let ab ; try { ab = await request ; this . audio . set ( uri , ab ) ; this . requests . delete ( uri ) ; lscore . requests . delete ( request ) ; if ( lscore . requests . size === 0 ) { dbg ( 'resolving LoadingScore for graph %s' , graphId ) ; lscore . signalLoaded ( ) ; this . scores . delete ( graphId ) ; } return ab ; } catch ( e ) { lscore . signalFailed ( e ) ; this . scores . delete ( graphId ) ; throw e ; } } scoreContentLoaded ( graphId ) { const lscore = this . scores . get ( graphId ) ; if ( ! lscore ) return Promise . resolve ( ) ; return lscore . loaded ; } }