Skip to content

Commit

Permalink
add indexeddb for JsBrowser
Browse files Browse the repository at this point in the history
replace localstorage with indexeddb
  • Loading branch information
frankois944 committed Feb 10, 2025
1 parent 82f66be commit d5de4a1
Show file tree
Hide file tree
Showing 9 changed files with 111 additions and 5 deletions.
7 changes: 4 additions & 3 deletions KtorKMPFileCaching/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ kotlin {

// Without okio Filesystem

val localStorageSystemMain by creating {
val browserStorageSystemMain by creating {
dependsOn(commonMain.get())
dependencies {
implementation(libs.kotlin.coroutines)
Expand All @@ -108,6 +108,7 @@ kotlin {

val jsBrowserMain by getting {
dependencies {
implementation(libs.indexeddb.core)
api(libs.okio.fakefilesystem)
}
}
Expand All @@ -117,8 +118,8 @@ kotlin {
api(libs.okio.fakefilesystem)
}

wasmJsMain.get().dependsOn(localStorageSystemMain)
jsBrowserMain.dependsOn(localStorageSystemMain)
wasmJsMain.get().dependsOn(browserStorageSystemMain)
jsBrowserMain.dependsOn(browserStorageSystemMain)

commonTest.dependencies {
implementation(libs.kotlin.test)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
@file:Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING")

package fr.frankois944.ktorfilecaching

internal expect object Database {
suspend fun getItem(key: String): String?

suspend fun setItem(
key: String,
value: String,
)

suspend fun removeItem(key: String)
}
Original file line number Diff line number Diff line change
Expand Up @@ -76,14 +76,14 @@ internal class FileCacheStorage(
val mutex = mutexes.computeIfAbsent(urlHex) { Mutex() }
mutex.withLock {
val serializedData = Cbor.encodeToHexString(caches.map { SerializableCachedResponseData(it) })
Window.setItem("${prefix}_$urlHex", serializedData)
Database.setItem("${prefix}_$urlHex", serializedData)
}
}

private suspend fun readCache(urlHex: String): Set<CachedResponseData> {
val mutex = mutexes.computeIfAbsent(urlHex) { Mutex() }
return mutex.withLock {
val item = Window.getItem("${prefix}_$urlHex")
val item = Database.getItem("${prefix}_$urlHex")
if (item == null) return@withLock emptySet()
try {
Cbor.decodeFromHexString<Set<SerializableCachedResponseData>>(item).map { it.cachedResponseData }.toSet()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
@file:Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING")

package fr.frankois944.ktorfilecaching

import com.juul.indexeddb.Database
import com.juul.indexeddb.Key
import com.juul.indexeddb.KeyPath
import com.juul.indexeddb.openDatabase
import fr.frankois944.ktorfilecaching.database.CacheItem

internal actual object Database {
private var database: Database? = null

private const val DATABASE_NAME = "KTorKmpFileCaching"
private const val STORE_NAME = "cacheitems"

suspend fun getDatabase(): Database {
if (database == null) {
database =
openDatabase(DATABASE_NAME, 1) { database, oldVersion, _ ->
if (oldVersion < 1) {
val store = database.createObjectStore(STORE_NAME, KeyPath("cacheKey"))
store.createIndex("cacheValue", KeyPath("cacheValue"), unique = false)
}
}
}
return database!!
}

actual suspend fun getItem(key: String): String? =
getDatabase()
.transaction(STORE_NAME) {
objectStore(STORE_NAME).get(Key(key)) as? CacheItem
}?.cacheValue

actual suspend fun setItem(
key: String,
value: String,
) {
getDatabase().writeTransaction(STORE_NAME) {
objectStore(STORE_NAME).put(
jso<CacheItem> {
cacheKey = key
cacheValue = value
},
)
}
}

actual suspend fun removeItem(key: String) {
getDatabase().writeTransaction(STORE_NAME) {
objectStore(STORE_NAME).delete(Key(key))
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package fr.frankois944.ktorfilecaching.database

internal external interface CacheItem {
var cacheKey: String
var cacheValue: String?
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
@file:Suppress("NOTHING_TO_INLINE")

package fr.frankois944.ktorfilecaching

internal inline fun <T : Any> jso(): T = js("({})")

internal inline fun <T : Any> jso(block: T.() -> Unit): T = jso<T>().apply(block)
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
@file:Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING")

package fr.frankois944.ktorfilecaching

import kotlinx.browser.window
import org.w3c.dom.set

internal actual object Database {
actual suspend fun getItem(key: String): String? = window.localStorage.getItem(key)

actual suspend fun setItem(
key: String,
value: String,
) {
window.localStorage[key] = value
}

actual suspend fun removeItem(key: String) {
window.localStorage.removeItem(key)
}
}
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@ slf4jJvm="2.0.16"
kotlinx-browser="0.3"
kotlinx-datetime="0.6.1"
publish="0.30.0"
indexeddb = "0.9.0"

[libraries]
collection = { module = "androidx.collection:collection", version.ref = "collection" }
indexeddb-core = { module = "com.juul.indexeddb:core", version.ref = "indexeddb" }
ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" }
ktor-client-logging = { module = "io.ktor:ktor-client-logging", version.ref = "ktor" }
ktor-client-mock = { module = "io.ktor:ktor-client-mock", version.ref = "ktor" }
Expand Down

0 comments on commit d5de4a1

Please sign in to comment.