import { LoadMessage , LoadResponse , LoginResponse , QueryMessage , QueryResponse , SaveMessage , SaveResponse } from './messages' ; export type SyncStatus = { sending : boolean ; retrying : boolean ; retryInSeconds : number ; warning : string ; } ; export class SyncStatusNotifier { private syncStatusHandlers : ( ( status : SyncStatus ) => void ) [ ] = [ ] ; onSyncStatus ( handler ) { this . syncStatusHandlers . push ( handler ) ; } notify ( status ) { this . syncStatusHandlers . forEach ( handler => { handler ( status ) ; } ) ; } } export interface HttpSuccess { result : "success" ; response : { } ; } export interface HttpFailure { result : "failure" ; error : string ; } export interface HttpRetry { result : "retry" ; error : string ; } export type HttpResponse = HttpSuccess | HttpFailure | HttpRetry ; export interface HttpConnection { get ( path ) : Promise < { } > ; post ( path , body , timeoutSeconds ) : Promise < HttpResponse > ; } function delay ( timeSeconds ) { return new Promise < void > ( ( resolve , reject ) => { setTimeout ( resolve , timeSeconds * 1000 ) ; } ) ; } export interface WebClientConfig { timeoutSeconds : number ; } export class WebClient { constructor ( private httpConnection , private syncStatusNotifier , private config ) { } async login ( ) { return < LoginResponse > await this . httpConnection . get ( '/login' ) ; } async query ( query ) { return < QueryResponse > await this . post ( '/query' , query ) ; } async save ( save ) { return < SaveResponse > await this . postWithRetry ( '/save' , save ) ; } async load ( load ) { return < LoadResponse > await this . post ( '/load' , load ) ; } private async post ( path , body ) { const response = await this . httpConnection . post ( path , body , this . config . timeoutSeconds ) ; if ( response . result === 'success' ) { return response . response ; } else { throw new Error ( response . error ) ; } } private async postWithRetry ( path , body ) { let timeoutSeconds = this . config . timeoutSeconds ; let retrySeconds = 1 ; while ( true ) { this . syncStatusNotifier . notify ( { sending : true , retrying : false , retryInSeconds : 0 , warning : '' } ) ; const response = await this . httpConnection . post ( path , body , timeoutSeconds ) ; if ( response . result === "success" ) { this . syncStatusNotifier . notify ( { sending : false , retrying : false , retryInSeconds : 0 , warning : '' } ) ; return response . response ; } else if ( response . result === "failure" ) { this . syncStatusNotifier . notify ( { sending : false , retrying : false , retryInSeconds : 0 , warning : response . error } ) ; throw new Error ( response . error ) ; } else { this . syncStatusNotifier . notify ( { sending : false , retrying : true , retryInSeconds : retrySeconds , warning : response . error } ) ; await delay ( retrySeconds ) ; timeoutSeconds = Math . min ( timeoutSeconds * 2 , 60 ) ; retrySeconds = Math . min ( retrySeconds * 2 , 60 ) ; } } } }