private param { Array } array param { Number } n return { Array } export function partition < T > ( array , n ) { if ( n <= 0 ) { return [ array ] ; } return array . reduce < T [ ] [ ] > ( ( acc , x , i ) => { if ( i === 0 || i % n === 0 ) { acc . push ( [ x ] ) ; } else { acc [ acc . length - 1 ] . push ( x ) ; } return acc ; } , [ ] ) ; } return export function delay ( ms ) { return new Promise ( ( resolve ) => setTimeout ( resolve , ms ) ) ; } interface Entity { id : string ; } private param { Object | String } resource return { Object } export function toDataBody < T extends Entity > ( resource ) { if ( isObject ( resource ) ) { return resource as T ; } if ( typeof resource === "string" ) { return { id : resource } ; } throw new Error ( "Invalid argument." ) ; } param { Object } obj return { String } export function qsify ( obj ) { const encode = ( v ) : string => encodeURIComponent ( typeof v === "boolean" ? String ( v ) : v ) ; const stripped = cleanUndefinedProperties ( obj ) ; return Object . keys ( stripped ) . map ( ( k ) => { const ks = encode ( k ) + "=" ; if ( Array . isArray ( stripped [ k ] ) ) { return ks + stripped [ k ] . map ( ( v ) => encode ( v ) ) . join ( "," ) ; } else { return ks + encode ( stripped [ k ] ) ; } } ) . join ( "&" ) ; } param { String } version param { String } minVersion param { String } maxVersion throws export function checkVersion ( version , minVersion , maxVersion ) { const extract = ( str ) : number [ ] => str . split ( "." ) . map ( ( x ) => parseInt ( x , 10 ) ) ; const [ verMajor , verMinor ] = extract ( version ) ; const [ minMajor , minMinor ] = extract ( minVersion ) ; const [ maxMajor , maxMinor ] = extract ( maxVersion ) ; const checks = [ verMajor < minMajor , verMajor === minMajor && verMinor < minMinor , verMajor > maxMajor , verMajor === maxMajor && verMinor >= maxMinor , ] ; if ( checks . some ( ( x ) => x ) ) { throw new Error ( `Version ${ version } doesn't satisfy ${ minVersion } <= x < ${ maxVersion }` ) ; } } type DecoratorReturn = ( target : any , key : string , descriptor : TypedPropertyDescriptor < ( ... args : any [ ] ) => any > ) => { configurable : boolean ; get ( ) : ( ... args : any ) => Promise < any > ; } ; param { String } min param { String } max return { Function } export function support ( min , max ) { return function ( target , key , descriptor ) { const fn = descriptor . value ; return { configurable : true , get ( ) { const wrappedMethod = ( ... args ) : Promise < any > => { const client = ( this as any ) . client ? ( this as any ) . client : this ; return client . fetchHTTPApiVersion ( ) . then ( ( version ) => checkVersion ( version , min , max ) ) . then ( ( ) => fn ! . apply ( this , args ) ) ; } ; Object . defineProperty ( this , key , { value : wrappedMethod , configurable : true , writable : true , } ) ; return wrappedMethod ; } , } ; } ; } param { Array < String > } capabilities return { Function } export function capable ( capabilities ) { return function ( target , key , descriptor ) { const fn = descriptor . value ; return { configurable : true , get ( ) { const wrappedMethod = ( ... args ) : Promise < any > => { const client = ( this as any ) . client ? ( this as any ) . client : this ; return client . fetchServerCapabilities ( ) . then ( ( available ) => { const missing = capabilities . filter ( ( c ) => ! ( c in available ) ) ; if ( missing . length > 0 ) { const missingStr = missing . join ( ", " ) ; throw new Error ( `Required capabilities ${ missingStr } not present on server` ) ; } } ) . then ( ( ) => fn ! . apply ( this , args ) ) ; } ; Object . defineProperty ( this , key , { value : wrappedMethod , configurable : true , writable : true , } ) ; return wrappedMethod ; } , } ; } ; } param { String } message return { Function } export function nobatch ( message ) { return function ( target , key , descriptor ) { const fn = descriptor . value ; return { configurable : true , get ( ) { const wrappedMethod = ( ... args ) : any => { if ( ( this as any ) . _isBatch ) { throw new Error ( message ) ; } return fn ! . apply ( this , args ) ; } ; Object . defineProperty ( this , key , { value : wrappedMethod , configurable : true , writable : true , } ) ; return wrappedMethod ; } , } ; } ; } param { Object } thing return { bool } export function isObject ( thing ) { return typeof thing === "object" && thing !== null && ! Array . isArray ( thing ) ; } interface TypedDataURL { type : string ; base64 : string ; [ key ] : string ; } param { String } dataURL return { Object } export function parseDataURL ( dataURL ) { const regex = /^data:(.*);base64,(.*)/ ; const match = dataURL . match ( regex ) ; if ( ! match ) { throw new Error ( `Invalid data-url: ${ String ( dataURL ) . substr ( 0 , 32 ) }...` ) ; } const props = match [ 1 ] ; const base64 = match [ 2 ] ; const [ type , ... rawParams ] = props . split ( ";" ) ; const params = rawParams . reduce < { [ key ] : string ; } > ( ( acc , param ) => { const [ key , value ] = param . split ( "=" ) ; return { ... acc , [ key ] : value } ; } , { } ) ; return { ... params , type , base64 } ; } param { String } dataURL return { Object } export function extractFileInfo ( dataURL ) { const { name , type , base64 } = parseDataURL ( dataURL ) ; const binary = atob ( base64 ) ; const array = [ ] ; for ( let i = 0 ; i < binary . length ; i ++ ) { array . push ( binary . charCodeAt ( i ) ) ; } const blob = new Blob ( [ new Uint8Array ( array ) ] , { type } ) ; return { blob , name } ; } param { String } dataURL param { Object } body param options param { Object } options . filename return { FormData } export function createFormData ( dataURL , body , options = { } ) { const { filename = "untitled" } = options ; const { blob , name } = extractFileInfo ( dataURL ) ; const formData = new FormData ( ) ; formData . append ( "attachment" , blob , name || filename ) ; for ( const property in body ) { if ( typeof body [ property ] !== "undefined" ) { formData . append ( property , JSON . stringify ( body [ property ] ) ) ; } } return formData ; } private export function cleanUndefinedProperties ( obj ) { const result = { } ; for ( const key in obj ) { if ( typeof obj [ key ] !== "undefined" ) { result [ key ] = obj [ key ] ; } } return result ; } param { String } path param { Array } options . fields param { Object } options . query export function addEndpointOptions ( path , options = { } ) { const query = { ... options . query } ; if ( options . fields ) { query . _fields = options . fields ; } const queryString = qsify ( query ) ; if ( queryString ) { return path + "?" + queryString ; } return path ; } /**
 * Replace authorization header with an obscured version
 */ export function obscureAuthorizationHeader ( headers ) { const h = new Headers ( headers ) ; if ( h . has ( "authorization" ) ) { h . set ( "authorization" , "**** (suppressed)" ) ; } const obscuredHeaders = { } ; for ( const [ header , value ] of h . entries ( ) ) { obscuredHeaders [ header ] = value ; } return obscuredHeaders ; }