@@ -395,6 +395,9 @@ export class IDBCache implements AsyncStorage {
395395
396396 const chunks : { index : number ; data : EncryptedChunk } [ ] = [ ] ;
397397
398+ let maxIndex = - 1 ;
399+ let lastChunkFound = false ;
400+
398401 for ( const chunkKey of chunkKeys ) {
399402 const encryptedData = await db . get ( this . storeName , chunkKey ) ;
400403 if ( ! encryptedData ) continue ;
@@ -406,6 +409,15 @@ export class IDBCache implements AsyncStorage {
406409 continue ;
407410 }
408411 const chunkIndex = parseChunkIndexFromKey ( chunkKey ) ;
412+ if ( chunkIndex > maxIndex ) {
413+ maxIndex = chunkIndex ;
414+ }
415+
416+ const isLastChunk = encryptedData . isLastChunk ?? false ;
417+ if ( isLastChunk ) {
418+ lastChunkFound = true ;
419+ }
420+
409421 chunks . push ( {
410422 index : chunkIndex ,
411423 data : encryptedData ,
@@ -414,6 +426,31 @@ export class IDBCache implements AsyncStorage {
414426
415427 if ( chunks . length === 0 ) return null ;
416428
429+ // Integrity check: Ensure that the last chunk is present and all preceding chunks are present
430+ if ( ! lastChunkFound ) {
431+ throw new IDBCacheError (
432+ `Integrity check failed for key ${ itemKey } : Last chunk is missing.`
433+ ) ;
434+ }
435+
436+ // Ensure all chunk indices from 0 to maxIndex are present
437+ if ( chunks . length !== maxIndex + 1 ) {
438+ throw new IDBCacheError (
439+ `Integrity check failed for key ${ itemKey } : Expected ${
440+ maxIndex + 1
441+ } chunks, but found ${ chunks . length } .`
442+ ) ;
443+ }
444+
445+ const indexSet = new Set ( chunks . map ( ( chunk ) => chunk . index ) ) ;
446+ for ( let i = 0 ; i <= maxIndex ; i ++ ) {
447+ if ( ! indexSet . has ( i ) ) {
448+ throw new IDBCacheError (
449+ `Integrity check failed for key ${ itemKey } : Missing chunk at index ${ i } .`
450+ ) ;
451+ }
452+ }
453+
417454 chunks . sort ( ( a , b ) => a . index - b . index ) ;
418455
419456 const decryptedChunks = await Promise . all (
@@ -429,6 +466,10 @@ export class IDBCache implements AsyncStorage {
429466
430467 return decryptedChunks . join ( "" ) ;
431468 } catch ( error ) {
469+ if ( error instanceof IDBCacheError ) {
470+ console . error ( `Integrity check failed for key ${ itemKey } :` , error ) ;
471+ throw error ;
472+ }
432473 if ( error instanceof DecryptionError ) {
433474 console . error ( `Decryption failed for key ${ itemKey } :` , error ) ;
434475 throw error ;
@@ -487,6 +528,8 @@ export class IDBCache implements AsyncStorage {
487528 } > = [ ] ;
488529 const chunksToUpdate : Array < EncryptedChunk > = [ ] ;
489530
531+ const totalChunks = Math . ceil ( value . length / this . chunkSize ) ;
532+
490533 for ( let i = 0 ; i < value . length ; i += this . chunkSize ) {
491534 const chunk = value . slice ( i , i + this . chunkSize ) ;
492535 const chunkIndex = Math . floor ( i / this . chunkSize ) ;
@@ -497,6 +540,8 @@ export class IDBCache implements AsyncStorage {
497540 const chunkKey = generateChunkKey ( baseKey , chunkIndex , chunkHash ) ;
498541 newChunkKeys . add ( chunkKey ) ;
499542
543+ const isLastChunk = chunkIndex === totalChunks - 1 ;
544+
500545 if ( existingChunkKeysSet . has ( chunkKey ) ) {
501546 const existingChunk = await db . get ( this . storeName , chunkKey ) ;
502547 if (
@@ -507,6 +552,7 @@ export class IDBCache implements AsyncStorage {
507552 ...existingChunk ,
508553 timestamp : expirationTimestamp ,
509554 cacheBuster : this . cacheBuster ,
555+ isLastChunk, // Update the flag in case it's the last chunk
510556 } ) ;
511557 }
512558 } else {
@@ -520,6 +566,7 @@ export class IDBCache implements AsyncStorage {
520566 encryptedChunk : {
521567 ...encryptedChunk ,
522568 cacheBuster : this . cacheBuster ,
569+ isLastChunk,
523570 } ,
524571 } ) ;
525572 }
0 commit comments