import { AxiosRequestConfig , AxiosResponse } from 'axios' ; export type CacheTargets = CacheTarget [ ] ; export enum CacheTarget { CACHE_API = 'CACHE_API' , MEMORY = 'MEMORY' } export type CacheResponse = { data : any ; status : number ; statusText : string ; headers : any ; } ; export const CACHE_API_KEY = 'sentinelhub-v1' ; export const SUPPORTED_TARGETS = [ CacheTarget . CACHE_API , CacheTarget . MEMORY ] ; const memoryCache = new Map ( ) ; export async function cacheFactory ( optionalTargets ) { const targets = optionalTargets || SUPPORTED_TARGETS ; const target = await getFirstUseableTarget ( targets ) ; return constructCache ( target ) ; } async function getFirstUseableTarget ( targets ) { let firstTargetToUse = undefined ; for ( const key of targets ) { if ( await doesTargetExist ( key ) ) { firstTargetToUse = key ; break ; } } return firstTargetToUse ; } async function checkIfCacheApiAvailable ( ) { if ( typeof window === 'undefined' || ! window . caches ) { return false ; } try { const caches = window . caches ; await caches . keys ( ) ; return true ; } catch ( err ) { console . warn ( 'CacheApi is not available' , err ) ; return false ; } } export async function doesTargetExist ( target ) { switch ( target ) { case CacheTarget . CACHE_API : return await checkIfCacheApiAvailable ( ) ; case CacheTarget . MEMORY : return true ; default : return false ; } } function constructCache ( target ) { switch ( target ) { case CacheTarget . CACHE_API : return new CacheApi ( ) ; case CacheTarget . MEMORY : return new MemoryCache ( ) ; default : return null ; } } interface ShCache { set ( key , response ) : Promise < void > ; get ( key , responseType ) : Promise < CacheResponse > ; has ( key ) : Promise < boolean > ; keys ( ) : Promise < readonly Request [ ] > | Promise < string [ ] > ; delete ( key ) : Promise < void > ; getHeaders ( key ) : Promise < Record < string , any > > ; invalidate ( ) : void ; } class MemoryCache implements ShCache { public async set ( key , response ) { memoryCache . set ( key , response ) ; } public async get ( key ) { const cachedResponse = memoryCache . get ( key ) ; if ( ! cachedResponse ) { return null ; } return { data : cachedResponse . data , status : cachedResponse . status , statusText : cachedResponse . statusText , headers : cachedResponse . headers , } ; } public async has ( key ) { return memoryCache . has ( key ) ; } public async keys ( ) { return Array . from ( memoryCache . keys ( ) ) ; } public async delete ( key ) { memoryCache . delete ( key ) ; } public async getHeaders ( key ) { const cachedResponse = memoryCache . get ( key ) ; if ( ! cachedResponse ) { return null ; } return cachedResponse . headers ; } public invalidate ( ) { memoryCache . clear ( ) ; } } class CacheApi implements ShCache { private cache : Record < string , any > ; public constructor ( ) { this . cache = caches . open ( CACHE_API_KEY ) ; } public async set ( key , response ) { const cache = await this . cache ; const responseData = this . serializeResponseData ( response ) ; cache . put ( key , responseData ) ; } public async get ( key , responseType ) { const cache = await this . cache ; const response = await cache . match ( key ) ; if ( ! response ) { return null ; } return { data : await this . deSerializeResponseData ( response , responseType ) , status : response . status , statusText : response . statusText , headers : await this . deserializeHeaders ( response . headers ) , } ; } public async has ( key ) { const cache = await this . cache ; const response = await cache . match ( key ) ; return Boolean ( response ) ; } public async keys ( ) { const cache = await caches . open ( CACHE_API_KEY ) ; const keys = await cache . keys ( ) ; return keys ; } public async delete ( key ) { const cache = await this . cache ; await cache . delete ( key ) ; } public async getHeaders ( key ) { const cache = await this . cache ; const response = await cache . match ( key ) ; if ( ! response ) { return null ; } return await this . deserializeHeaders ( response . headers ) ; } public async invalidate ( ) { const cache = await this . cache ; const cacheKeys = await cache . keys ( ) ; for ( let key of cacheKeys ) { await cache . delete ( key ) ; } } private serializeResponseData ( response ) { let responseData ; switch ( response . config . responseType ) { case 'blob' : case 'arraybuffer' : case 'text' : responseData = response . data ; break ; case 'json' : case undefined : responseData = JSON . stringify ( response . data ) ; break ; default : throw new Error ( 'Unsupported response type: ' + response . request . responseType ) ; } return new Response ( responseData , response ) ; } private async deSerializeResponseData ( cachedResponse , responseType ) { switch ( responseType ) { case 'blob' : return await cachedResponse . clone ( ) . blob ( ) ; case 'arraybuffer' : return await cachedResponse . clone ( ) . arrayBuffer ( ) ; case 'text' : return await cachedResponse . clone ( ) . text ( ) ; case 'json' : case undefined : return await cachedResponse . clone ( ) . json ( ) ; default : throw new Error ( 'Unsupported response type: ' + responseType ) ; } } private async deserializeHeaders ( headers ) { const newHeaders = { } ; for ( let key of headers . keys ( ) ) { newHeaders [ key ] = headers . get ( key ) ; } return newHeaders ; } } export async function invalidateCaches ( optionalTargets ? ) { const targets = optionalTargets || SUPPORTED_TARGETS ; for ( const target of targets ) { if ( ! ( await doesTargetExist ( target ) ) ) { continue ; } switch ( target ) { case CacheTarget . CACHE_API : await new CacheApi ( ) . invalidate ( ) ; case CacheTarget . MEMORY : await new MemoryCache ( ) . invalidate ( ) ; default : break ; } } }