@@ -1052,6 +1052,169 @@ export class ArtifactCache implements ArtifactCacheTemplate {
10521052 }
10531053}
10541054
1055+ /**
1056+ * Cache by IndexDB to support caching model data
1057+ */
1058+ export class ArtifactIndexDBCache implements ArtifactCacheTemplate {
1059+ private dbName ?: string ;
1060+ private dbVersion = 1 ;
1061+ private db : IDBDatabase | undefined ;
1062+
1063+ private async initDB ( ) {
1064+ if ( this . db != null ) {
1065+ return ; // the db is already inialized
1066+ }
1067+ return new Promise < void > ( ( resolve , reject ) => {
1068+ const request = indexedDB . open ( this . dbName , this . dbVersion ) ;
1069+ request . onupgradeneeded = ( event ) => {
1070+ this . db = ( event . target as IDBOpenDBRequest ) . result ;
1071+ if ( ! this . db . objectStoreNames . contains ( 'urls' ) ) {
1072+ this . db . createObjectStore ( 'urls' , { keyPath : 'url' } ) ;
1073+ }
1074+ } ;
1075+ request . onsuccess = ( event ) => {
1076+ this . db = ( event . target as IDBOpenDBRequest ) . result ;
1077+ resolve ( ) ;
1078+ } ;
1079+ request . onerror = ( event ) => {
1080+ console . error ( "Database error: " , ( event . target as IDBOpenDBRequest ) . error ) ;
1081+ reject ( ( event . target as IDBOpenDBRequest ) . error ) ;
1082+ } ;
1083+ } ) ;
1084+ }
1085+
1086+ /* Check if the URL is in DB or not */
1087+ private async isUrlInDB ( url : string ) : Promise < boolean > {
1088+ return new Promise < boolean > ( ( resolve , reject ) => {
1089+ const transaction = this . db ?. transaction ( [ 'urls' ] , 'readonly' ) ;
1090+ if ( transaction === undefined ) {
1091+ return false ;
1092+ }
1093+ const store = transaction . objectStore ( 'urls' ) ;
1094+ const request = store . get ( url ) ;
1095+ request . onsuccess = ( ) => {
1096+ resolve ( request . result !== undefined ) ;
1097+ } ;
1098+ request . onerror = ( event ) => {
1099+ reject ( ( event . target as IDBRequest ) . error ) ;
1100+ } ;
1101+ } ) ;
1102+ }
1103+
1104+ constructor ( dbName : string ) {
1105+ this . dbName = dbName ;
1106+ }
1107+
1108+ async asyncGetHelper ( url : string ) {
1109+ return new Promise ( ( resolve , reject ) => {
1110+ let result : any ;
1111+ const transaction = this . db ?. transaction ( [ 'urls' ] , 'readonly' ) ;
1112+ if ( transaction === undefined ) {
1113+ return false ;
1114+ }
1115+ transaction . oncomplete = _ => resolve ( result ) ;
1116+ transaction . onerror = e => reject ( transaction . error ) ;
1117+ const objectStore = transaction . objectStore ( 'urls' ) ;
1118+ const getRequest = objectStore . get ( url ) ;
1119+ getRequest . onsuccess = ( ) => {
1120+ result = getRequest . result ;
1121+ }
1122+ } )
1123+ }
1124+
1125+ async fetchWithCache ( url : string ) {
1126+ await this . initDB ( ) ; // await the initDB process
1127+ const isInDB = await this . isUrlInDB ( url ) ;
1128+ if ( ! isInDB ) {
1129+ const response = await this . addToCache ( url ) ;
1130+ return response ;
1131+ } else {
1132+ const result = await this . asyncGetHelper ( url ) ;
1133+ if ( result != null && typeof result === "object" && "data" in result ) {
1134+ return result . data ;
1135+ }
1136+ return null ;
1137+ }
1138+ }
1139+
1140+ async addToIndexDB ( url : string , response : any ) {
1141+ await this . initDB ( ) ;
1142+ const data = await response . json ( ) ;
1143+ return new Promise < void > ( ( resolve , reject ) => {
1144+ const transaction = this . db ?. transaction ( [ 'urls' ] , 'readwrite' ) ;
1145+ if ( transaction === undefined ) {
1146+ return ;
1147+ }
1148+ const store = transaction . objectStore ( 'urls' ) ;
1149+ const request = store . add ( { data, url} ) ; // Index DB follows a {value, key} format, instead of {key, value} format!
1150+ request . onsuccess = ( ) => resolve ( ) ;
1151+ request . onerror = ( event ) => reject ( ( event . target as IDBRequest ) . error ) ;
1152+ } ) ;
1153+ }
1154+
1155+ async addToCache ( url : string ) :Promise < any > {
1156+ let response : Response ;
1157+ try {
1158+ response = await fetch ( url ) ;
1159+ if ( ! response . ok ) {
1160+ throw new Error ( 'Network response was not ok' ) ;
1161+ }
1162+ const response_copy = response . clone ( ) ;
1163+ await this . addToIndexDB ( url , response_copy ) ;
1164+ return response ;
1165+ } catch ( error ) {
1166+ console . error ( "There was a problem fetching the data:" , error ) ;
1167+ }
1168+ }
1169+
1170+ async hasAllKeys ( keys : string [ ] ) {
1171+ await this . initDB ( ) ; // Ensure the DB is initialized
1172+ return new Promise < boolean > ( ( resolve , reject ) => {
1173+ const transaction = this . db ?. transaction ( [ 'urls' ] , 'readonly' ) ;
1174+ if ( transaction === undefined ) {
1175+ return ;
1176+ }
1177+ const store = transaction . objectStore ( 'urls' ) ;
1178+ let allExist = true ;
1179+ for ( const key of keys ) {
1180+ const request = store . get ( key ) ;
1181+ // Wait for each request to complete before moving to the next to ensure accurate allExist status
1182+ new Promise < void > ( ( resolve ) => {
1183+ request . onsuccess = ( ) => {
1184+ if ( request . result === undefined ) {
1185+ allExist = false ;
1186+ }
1187+ resolve ( ) ;
1188+ } ;
1189+ request . onerror = ( ) => {
1190+ allExist = false ;
1191+ resolve ( ) ; // Resolve to continue checking other keys even if one fails
1192+ } ;
1193+ } ) ;
1194+ if ( ! allExist ) {
1195+ break ;
1196+ }
1197+ }
1198+ resolve ( allExist ) ;
1199+ } ) ;
1200+ }
1201+
1202+ async deleteInCache ( url : string ) {
1203+ await this . initDB ( ) ; // Make sure the DB is initialized
1204+ const transaction = this . db ?. transaction ( [ 'urls' ] , 'readwrite' ) ;
1205+ if ( transaction === undefined ) {
1206+ return ;
1207+ }
1208+ const store = transaction . objectStore ( 'urls' ) ;
1209+ const request = store . delete ( url ) ;
1210+ await new Promise < void > ( ( resolve , reject ) => {
1211+ request . onsuccess = ( ) => resolve ( ) ;
1212+ request . onerror = ( ) => reject ( request . error ) ;
1213+ } ) ;
1214+ return ;
1215+ }
1216+ }
1217+
10551218/**
10561219 * TVM runtime instance.
10571220 *
0 commit comments