module import XHRPromise , { XHROptions } from '../libs/xhr-promise' ; import Config from './config' ; import { DebugOptions } from './debug' ; import { Transaction } from './purchase-client' ; import { AccumulateData , EligibleCodes , ExtensionUsersResult , RankResponse , RedeemedCodes , RedeemResult , TriviaLeaderboard , TriviaOption , TriviaQuestion , TriviaQuestionResponse , TriviaQuestionState , TriviaStateResponse , TriviaTeam , UserInfo , VoteData , VoteLog } from './sdk' ; import { TwitchAuth } from './twitch' ; import { Environment } from './util' ; /**
 * API URL to use for backend requests.
 * Configure using using  setEnvironment .
 *  ignore let SERVER_URL ; let FAKEAUTH_URL ; ignore export enum ServerState { ALL = 'all_state' , AUTHENTICATION = 'authentication' , CHANNEL = 'channel_state' , EXTENSION = 'extension_state' , EXTENSION_SECRET = 'extension_hidden_state' , EXTENSION_VIEWER = 'extension_viewer_state' , USER = 'user_info' , VIEWER = 'viewer_state' } ignore export enum ServerConfig { ALL = 'config' , CHANNEL = 'config/channel' , EXTENSION = 'config/extension' } ignore export interface Hook < HookCallback > { failure : HookCallback ; success : HookCallback ; } export type RequestHook = ( config : XHROptions ) => Promise < XHROptions > ; export type ResponseHook = < InType = unknown , OutType = unknown > ( resp : InType ) => Promise < OutType > ; ignore export class HookManager < HookCallback > implements Iterable < Hook < HookCallback > > { private callbacks : Array < { failure : HookCallback ; success : HookCallback ; } > = [ ] ; public get length ( ) { return this . callbacks . length ; } public add ( success , failure ? ) { this . callbacks . push ( { failure , success } ) ; return this . callbacks . length - 1 ; } public remove ( index ) { if ( this . callbacks [ index ] ) { this . callbacks [ index ] = null ; } } public [ Symbol . iterator ] ( ) { let current = 0 ; const callbacks = this . callbacks ; return { next ( ) { while ( callbacks [ current ] === null && current < callbacks . length ) { current ++ ; } if ( current < callbacks . length ) { return { done : false , value : callbacks [ current ++ ] } ; } else { return { done : true , value : null } ; } } } ; } } /**
 * Wraps all extension backend accessor and mutator endpoints in convenience functions.
 *
 * Should not normally be created directly, instead an instance is made available
 * and namespaced appropriately when using  Muxy . SDK .
 *
 *  private example class StateClient { ignore public static async fetchTestAuth ( extensionID , debug ) { const data = JSON . stringify ( { app_id : extensionID , channel_id : debug . channelID , role : debug . role , user_id : debug . userID || '12345678' } ) ; const xhr = new XHRPromise ( { data , headers : { 'Content-Type' : 'application/json' } , method : 'POST' , url : `${ debug . url || SERVER_URL }/v1/e/authtoken?role=${ debug . role }` } ) ; return xhr . send ( ) . then ( ( resp ) => { if ( resp && resp . status < 400 ) { this . setEnvironment ( null , debug ) ; const auth = Object . assign ( new TwitchAuth ( ) , resp . responseText , { channelId : debug . channelID , clientId : extensionID , userId : debug . userID ? `U${ debug . userID }` : 'U12345678' } ) ; return Promise . resolve ( auth ) ; } else { return Promise . reject ( resp . statusText ) ; } } ) ; } ignore public static setEnvironment ( env , debug ) { if ( env ) { const urls = Config . GetServerURLs ( env ) ; SERVER_URL = urls . ServerURL ; FAKEAUTH_URL = urls . FakeAuthURL ; } if ( debug && debug . url ) { SERVER_URL = debug . url ; FAKEAUTH_URL = debug . url ; } } public token : string ; public debug : DebugOptions ; public hooks = { requests : new HookManager < RequestHook > ( ) , responses : new HookManager < ResponseHook > ( ) } ; private loaded : Promise < void > ; ignore constructor ( loadedPromise , debug ) { ignore this . token = null ; this . debug = debug ; this . loaded = loadedPromise ; } ignore public updateAuth ( token ) { this . token = token ; } ignore public async signedRequest < DataType = unknown , ResponseType = unknown > ( extensionID , method , endpoint , data ? , skipPromise ? ) { let promise = skipPromise ? Promise . resolve ( undefined ) : this . loaded ; const chain = [ async ( config ) : Promise < XHROptions > => { if ( ! this . validateJWT ( ) ) { return Promise . reject ( 'Your authentication token has expired.' ) ; } const xhrPromise = new XHRPromise ( config ) ; try { const resp = await xhrPromise . send ( ) ; if ( resp . status < 400 ) { return Promise . resolve ( resp . responseText ) ; } else if ( resp . responseText ) { return Promise . reject ( resp . responseText ) ; } else { return Promise . reject ( `Server returned status ${ resp . status }` ) ; } } catch ( requestErr ) { return Promise . reject ( requestErr ) ; } } , undefined ] ; for ( const h of this . hooks . requests ) { chain . unshift ( h . success , h . failure ) ; } for ( const h of this . hooks . responses ) { chain . push ( h . success , h . failure ) ; } promise = promise . then ( ( ) => ( { data : JSON . stringify ( data ) , headers : { Authorization : `${ extensionID } ${ this . token }` } , method , url : `${ SERVER_URL }/v1/e/${ endpoint }` } ) ) ; while ( chain . length ) { promise = promise . then ( chain . shift ( ) , chain . shift ( ) ) ; } return promise . then ( ( value ) => { return value as ResponseType ; } ) ; } ignore public validateJWT ( ) { try { const splitToken = this . token . split ( '.' ) ; if ( splitToken . length !== 3 ) { return false ; } const tk = JSON . parse ( atob ( splitToken [ 1 ] ) ) ; if ( ! tk . exp ) { return false ; } const now = new Date ( ) . valueOf ( ) ; if ( tk . exp < now / 1000 ) { return false ; } return true ; } catch ( err ) { return false ; } } ignore public getState = < ResponseType = unknown > ( identifier , substate ? ) : Promise < ResponseType > => this . signedRequest < undefined , ResponseType > ( identifier , 'GET' , substate || ServerState . ALL ) ; ignore public getConfig = < ResponseType = unknown > ( identifier , subconfig ? ) : Promise < ResponseType > => this . signedRequest < undefined , ResponseType > ( identifier , 'GET' , subconfig || ServerConfig . ALL ) ; ignore public postState = < DataType = unknown , ResponseType = unknown > ( identifier , substate , data ) => this . signedRequest < DataType , ResponseType > ( identifier , 'POST' , substate || ServerState . ALL , data ) ; ignore public postConfig = < DataType = unknown , ResponseType = unknown > ( identifier , subconfig , data ) => this . signedRequest < DataType , ResponseType > ( identifier , 'POST' , subconfig || ServerConfig . ALL , data ) ; ignore public getUserInfo = ( identifier ) => this . getState ( identifier , ServerState . USER ) ; ignore public immediateGetUserInfo = ( identifier ) => this . signedRequest < undefined , UserInfo > ( identifier , 'GET' , ServerState . USER , undefined , true ) ; ignore public getViewerState = < ResponseType = unknown > ( identifier ) => this . getState < ResponseType > ( identifier , ServerState . VIEWER ) ; ignore public getExtensionViewerState = < ResponseType = unknown > ( identifier ) => this . getState < ResponseType > ( identifier , ServerState . EXTENSION_VIEWER ) ; ignore public multiGetExtensionViewerState = < ResponseType = unknown > ( identifier , users ) => this . signedRequest < undefined , ResponseType > ( identifier , 'GET' , `extension_viewer_state?user_ids=${ users . join ( ',' ) }` ) ; ignore public getExtensionSecretState = < ResponseType = unknown > ( identifier ) => this . getState < ResponseType > ( identifier , ServerState . EXTENSION_SECRET ) ; ignore public getChannelState = < ResponseType = unknown > ( identifier ) => this . getState < ResponseType > ( identifier , ServerState . CHANNEL ) ; ignore public getServerConfig = < ResponseType = unknown > ( identifier ) => this . getConfig < ResponseType > ( identifier , ServerConfig . ALL ) ; ignore public getChannelConfig = < ResponseType = unknown > ( identifier ) => this . getConfig < ResponseType > ( identifier , ServerConfig . CHANNEL ) ; ignore public getExtensionConfig = < ResponseType = unknown > ( identifier ) => this . getConfig < ResponseType > ( identifier , ServerConfig . EXTENSION ) ; ignore public getExtensionState = < ResponseType = unknown > ( identifier ) => this . getState < ResponseType > ( identifier , ServerState . EXTENSION ) ; ignore public setExtensionState = < DataType = unknown , ResponseType = unknown > ( identifier , state ) => this . postState < DataType , ResponseType > ( identifier , ServerState . EXTENSION , state ) ; ignore public patchExtensionState = < DataType = unknown , ResponseType = unknown > ( identifier , multiState ) => this . signedRequest < DataType , ResponseType > ( identifier , 'PATCH' , ServerState . EXTENSION , multiState ) ; ignore public setViewerState = < DataType = unknown , ResponseType = unknown > ( identifier , state ) => this . postState < DataType , ResponseType > ( identifier , ServerState . VIEWER , state ) ; ignore public patchViewerState = < DataType = unknown , ResponseType = unknown > ( identifier , multiState ) => this . signedRequest < DataType , ResponseType > ( identifier , 'PATCH' , ServerState . VIEWER , multiState ) ; ignore public setExtensionViewerState = < DataType = unknown , ResponseType = unknown > ( identifier , state ) => this . postState < DataType , ResponseType > ( identifier , ServerState . EXTENSION_VIEWER , state ) ; ignore public patchExtensionViewerState = < DataType = unknown , ResponseType = unknown > ( identifier , multiState ) => this . signedRequest < DataType , ResponseType > ( identifier , 'PATCH' , ServerState . EXTENSION_VIEWER , multiState ) ; ignore public setExtensionSecretState = < DataType = unknown , ResponseType = unknown > ( identifier , state ) => this . postState < DataType , ResponseType > ( identifier , ServerState . EXTENSION_SECRET , state ) ; ignore public patchExtensionSecretState = < DataType = unknown , ResponseType = unknown > ( identifier , multiState ) => this . signedRequest < DataType , ResponseType > ( identifier , 'PATCH' , ServerState . EXTENSION_SECRET , multiState ) ; ignore public setChannelState = < DataType = unknown , ResponseType = unknown > ( identifier , state ) => this . postState < DataType , ResponseType > ( identifier , ServerState . CHANNEL , state ) ; ignore public patchChannelState = < DataType = unknown , ResponseType = unknown > ( identifier , multiState ) => this . signedRequest < DataType , ResponseType > ( identifier , 'PATCH' , ServerState . CHANNEL , multiState ) ; ignore public setChannelConfig = < DataType = unknown , ResponseType = unknown > ( identifier , config ) => this . postConfig < DataType , ResponseType > ( identifier , ServerConfig . CHANNEL , config ) ; ignore public patchChannelConfig = < DataType = unknown , ResponseType = unknown > ( identifier , multiConfig ) => this . signedRequest < DataType , ResponseType > ( identifier , 'PATCH' , ServerConfig . CHANNEL , multiConfig ) ; ignore public setExtensionConfig = < DataType = unknown , ResponseType = unknown > ( identifier , config ) => this . postConfig < DataType , ResponseType > ( identifier , ServerConfig . EXTENSION , config ) ; ignore public patchExtensionConfig = < DataType = unknown , ResponseType = unknown > ( identifier , multiConfig ) => this . signedRequest < DataType , ResponseType > ( identifier , 'PATCH' , ServerConfig . EXTENSION , multiConfig ) ; ignore public getAccumulation = ( identifier , id , start ) => this . signedRequest < undefined , AccumulateData > ( identifier , 'GET' , `accumulate?id=${ id || 'default' }&start=${ start }` ) ; ignore public accumulate = < DataType = unknown , ResponseType = unknown > ( identifier , id , data ) => this . signedRequest < DataType , ResponseType > ( identifier , 'POST' , `accumulate?id=${ id || 'default' }` , data ) ; ignore public vote = < DataType = unknown > ( identifier , id , data ) => this . signedRequest < DataType , VoteData > ( identifier , 'POST' , `vote?id=${ id || 'default' }` , data ) ; ignore public getVotes = ( identifier , id = 'default' ) => this . signedRequest < undefined , VoteData > ( identifier , 'GET' , `vote?id=${ id }` ) ; ignore public getFullVoteLogs = ( identifier , id = 'default' ) => this . signedRequest < undefined , VoteLog > ( identifier , 'GET' , `vote_logs?id=${ id }` ) ; ignore public rank = < DataType = unknown > ( identifier , id , data ) => this . signedRequest < DataType , RankResponse > ( identifier , 'POST' , `rank?id=${ id || 'default' }` , data ) ; ignore public getRank = async < ResponseType = unknown > ( identifier , id = 'default' ) => this . signedRequest < undefined , ResponseType > ( identifier , 'GET' , `rank?id=${ id }` ) ; ignore public deleteRank = < ResponseType = unknown > ( identifier , id = 'default' ) => this . signedRequest < undefined , ResponseType > ( identifier , 'DELETE' , `rank?id=${ id }` ) ; ignore public getJSONStore = < ResponseType = unknown > ( identifier , id = 'default' ) => this . signedRequest < undefined , ResponseType > ( identifier , 'GET' , `json_store?id=${ id }` ) ; ignore public validateCode = < ResponseType = unknown > ( identifier , code ) => this . signedRequest < { pin : string ; } , ResponseType > ( identifier , 'POST' , 'validate_pin' , { pin : code } ) ; ignore public pinTokenExists = < ResponseType = unknown > ( identifier ) => this . signedRequest < undefined , ResponseType > ( identifier , 'GET' , 'pin_token_exists' ) ; ignore public revokeAllPINCodes = < ResponseType = unknown > ( identifier ) => this . signedRequest < undefined , ResponseType > ( identifier , 'DELETE' , 'pin' ) ; ignore public getEligibleCodes = ( identifier ) => this . signedRequest < undefined , EligibleCodes > ( identifier , 'GET' , 'codes/eligible' ) ; ignore public getRedeemedCodes = ( identifier ) => this . signedRequest < undefined , RedeemedCodes > ( identifier , 'GET' , 'codes/redeemed' ) ; ignore public redeemCode = ( identifier , prizeIndex ) => this . signedRequest < { all_prizes : number ; } , RedeemResult > ( identifier , 'POST' , 'codes/redeem' , { all_prizes : prizeIndex } ) ; ignore public getExtensionUsers = ( identifier , cursor ) => this . signedRequest < undefined , ExtensionUsersResult > ( identifier , 'GET' , `user_ids?cursor=${ cursor || 0 }` ) ; ignore public joinExtensionTriviaTeam = ( identifier ) => this . signedRequest ( identifier , 'POST' , 'team_membership' ) ; ignore public getExtensionTriviaJoinedTeam = ( identifier ) => this . signedRequest < undefined , TriviaTeam > ( identifier , 'GET' , 'team_membership' ) ; ignore public addExtensionTriviaQuestion = ( identifier , triviaQuestion ) : Promise < Record < string , unknown > > => this . signedRequest < TriviaQuestion , Record < string , unknown > > ( identifier , 'POST' , 'curated_poll_edit' , triviaQuestion ) ; ignore public removeExtensionTriviaQuestion = ( identifier , triviaQuestionID ) : Promise < Record < string , unknown > > => this . signedRequest < { id : string ; } , Record < string , unknown > > ( identifier , 'DELETE' , 'curated_poll_edit' , { id : triviaQuestionID } ) ; ignore public addExtensionTriviaOptionToQuestion = ( identifier , questionID , option ) => this . signedRequest < { id : string ; option : TriviaOption ; } , TriviaQuestion > ( identifier , 'POST' , 'curated_poll_edit_option' , { id : questionID , option } ) ; ignore public removeExtensionTriviaOptionFromQuestion = ( identifier , questionID , optionID ) => this . signedRequest < { question : string ; option : string ; } , TriviaQuestion > ( identifier , 'DELETE' , 'curated_poll_edit_option' , { question : questionID , option : optionID } ) ; ignore public setExtensionTriviaQuestionState = ( identifier , questionID , state , winner ) => this . signedRequest < { transition : TriviaQuestionState ; winner : string ; } , TriviaStateResponse > ( identifier , 'POST' , `curated_poll_state?id=${ questionID }` , { transition : state , winner } ) ; ignore public setExtensionTriviaQuestionVote = ( identifier , questionID , optionID ) : Promise < Record < string , unknown > > => this . signedRequest < { question_id : string ; vote : string ; } , Record < string , unknown > > ( identifier , 'POST' , 'curated_poll' , { question_id : questionID , vote : optionID } ) ; ignore public getExtensionTriviaQuestions = ( identifier ) => this . signedRequest < undefined , TriviaQuestionResponse > ( identifier , 'GET' , 'curated_poll' ) ; ignore public getExtensionTriviaQuestion = ( identifier , questionID ) => this . signedRequest < undefined , TriviaQuestion > ( identifier , 'GET' , `curated_poll?id=${ questionID }` ) ; ignore public getExtensionTriviaLeaderboard = ( identifer ) => this . signedRequest < undefined , TriviaLeaderboard > ( identifer , 'GET' , 'curated_poll_leaderboard' ) ; ignore public sendTransactionToServer = ( identifier , tx ) => this . signedRequest < Transaction , Record < string , unknown > > ( identifier , 'POST' , 'bits/transactions' , tx ) ; } export default StateClient ;