diff --git a/core/cryptography/build.gradle.kts b/core/cryptography/build.gradle.kts index 4d2b19ec3273..394655b13308 100644 --- a/core/cryptography/build.gradle.kts +++ b/core/cryptography/build.gradle.kts @@ -111,7 +111,11 @@ kotlin { } val jsMain by getting - val jsTest by getting + val jsTest by getting { + dependencies { + implementation(devNpm("fake-indexeddb", "6.2.5")) + } + } } } diff --git a/core/cryptography/src/jsTest/kotlin/com/wire/kalium/cryptography/IndexedDbTestBootstrap.kt b/core/cryptography/src/jsTest/kotlin/com/wire/kalium/cryptography/IndexedDbTestBootstrap.kt new file mode 100644 index 000000000000..9dc1995331e7 --- /dev/null +++ b/core/cryptography/src/jsTest/kotlin/com/wire/kalium/cryptography/IndexedDbTestBootstrap.kt @@ -0,0 +1,28 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +@file:Suppress("unused") + +package com.wire.kalium.cryptography + +@JsModule("fake-indexeddb/auto") +@JsNonModule +private external val fakeIndexedDbAuto: dynamic + +// Force module side effects so js tests expose indexedDB in the Node-based runner. +private val indexedDbPolyfill = fakeIndexedDbAuto diff --git a/core/cryptography/src/webMain/kotlin/com/wire/kalium/cryptography/PlatformFileUtils.web.kt b/core/cryptography/src/webMain/kotlin/com/wire/kalium/cryptography/PlatformFileUtils.web.kt index 77bf9ccde701..3f897f3a30cb 100644 --- a/core/cryptography/src/webMain/kotlin/com/wire/kalium/cryptography/PlatformFileUtils.web.kt +++ b/core/cryptography/src/webMain/kotlin/com/wire/kalium/cryptography/PlatformFileUtils.web.kt @@ -17,14 +17,31 @@ */ package com.wire.kalium.cryptography +// CoreCrypto web persists its own state in IndexedDB. These filesystem helpers are only +// relevant for native file-based storage/migration paths and should stay inert on JS. internal actual fun createDirectory(path: String): Boolean { - TODO("Not yet implemented") + logJsFilesystemShimUsageOnce("createDirectory", path) + return true } internal actual fun fileExists(path: String): Boolean { - TODO("Not yet implemented") + logJsFilesystemShimUsageOnce("fileExists", path) + return false } internal actual fun deleteFile(path: String): Boolean { - TODO("Not yet implemented") + logJsFilesystemShimUsageOnce("deleteFile", path) + return false +} + +private var hasLoggedJsFilesystemShimUsage = false + +private fun logJsFilesystemShimUsageOnce(operation: String, path: String) { + if (hasLoggedJsFilesystemShimUsage) return + + hasLoggedJsFilesystemShimUsage = true + kaliumLogger.w( + "JS filesystem shim invoked via '$operation' for path '$path'. " + + "CoreCrypto web uses IndexedDB-backed persistence, so file-based helpers stay inert on JS." + ) } diff --git a/core/util/src/androidMain/kotlin/com.wire.kalium.util/FileUtil.kt b/core/util/src/androidMain/kotlin/com.wire.kalium.util/FileUtil.kt index 13105bd72887..eb4fca0eaefc 100644 --- a/core/util/src/androidMain/kotlin/com.wire.kalium.util/FileUtil.kt +++ b/core/util/src/androidMain/kotlin/com.wire.kalium.util/FileUtil.kt @@ -27,6 +27,8 @@ actual object FileUtil { return File(path).deleteRecursively() } + actual suspend fun deletePersistentDirectory(path: String): Boolean = deleteDirectory(path) + actual fun isDirectoryNonEmpty(path: String): Boolean { return File(path).listFiles()?.isNotEmpty() ?: false } diff --git a/core/util/src/appleMain/kotlin/com.wire.kalium.util/FileUtil.kt b/core/util/src/appleMain/kotlin/com.wire.kalium.util/FileUtil.kt index 9350fcbb9cef..c42588b6023b 100644 --- a/core/util/src/appleMain/kotlin/com.wire.kalium.util/FileUtil.kt +++ b/core/util/src/appleMain/kotlin/com.wire.kalium.util/FileUtil.kt @@ -36,6 +36,8 @@ actual object FileUtil { return NSFileManager.defaultManager.removeItemAtPath(path, error.ptr) } + actual suspend fun deletePersistentDirectory(path: String): Boolean = deleteDirectory(path) + actual fun isDirectoryNonEmpty(path: String): Boolean = memScoped { val error = alloc>() NSFileManager.defaultManager.contentsOfDirectoryAtPath(path, error.ptr)?.isNotEmpty() ?: false diff --git a/core/util/src/commonMain/kotlin/com.wire.kalium.util/FileUtil.kt b/core/util/src/commonMain/kotlin/com.wire.kalium.util/FileUtil.kt index 4cf1b5c52564..8e4bb8e6a55b 100644 --- a/core/util/src/commonMain/kotlin/com.wire.kalium.util/FileUtil.kt +++ b/core/util/src/commonMain/kotlin/com.wire.kalium.util/FileUtil.kt @@ -23,5 +23,7 @@ expect object FileUtil { fun deleteDirectory(path: String): Boolean + suspend fun deletePersistentDirectory(path: String): Boolean + fun isDirectoryNonEmpty(path: String): Boolean } diff --git a/core/util/src/jsMain/kotlin/com.wire.kalium.util/FileUtil.kt b/core/util/src/jsMain/kotlin/com.wire.kalium.util/FileUtil.kt index 2cba7a0bc908..cbf5043b31c2 100644 --- a/core/util/src/jsMain/kotlin/com.wire.kalium.util/FileUtil.kt +++ b/core/util/src/jsMain/kotlin/com.wire.kalium.util/FileUtil.kt @@ -18,16 +18,50 @@ package com.wire.kalium.util +import kotlinx.coroutines.await +import kotlin.js.Promise + actual object FileUtil { actual fun mkDirs(path: String): Boolean { - TODO("Not yet implemented") + // Web targets do not expose a host filesystem here; crypto persistence is handled elsewhere. + return path.isNotBlank() } actual fun deleteDirectory(path: String): Boolean { - TODO("Not yet implemented") + return path.isNotBlank() + } + + actual suspend fun deletePersistentDirectory(path: String): Boolean { + val indexedDb = js("window.indexedDB") + return when { + path.isBlank() -> false + indexedDb == null -> false + js("typeof indexedDb.databases !== 'function'") as Boolean -> deleteIndexedDb(indexedDb, path).await() + else -> { + val databases = indexedDb.databases().unsafeCast>>().await() + val matchingNames = databases + .mapNotNull { it?.name as? String } + .filter { name -> name == path || name.startsWith("$path/") } + + matchingNames.isEmpty() || matchingNames.map { deleteIndexedDb(indexedDb, it).await() }.all { it } + } + } } actual fun isDirectoryNonEmpty(path: String): Boolean { - TODO("Not yet implemented") + return false + } +} + +private fun deleteIndexedDb(indexedDb: dynamic, name: String): Promise = Promise { resolve, _ -> + val request = indexedDb.deleteDatabase(name) + request.onsuccess = { + resolve(true) + } + request.onerror = { + resolve(false) + } + request.onblocked = { + resolve(false) } } diff --git a/core/util/src/jvmMain/kotlin/com/wire/kalium/util/FileUtil.kt b/core/util/src/jvmMain/kotlin/com/wire/kalium/util/FileUtil.kt index 13105bd72887..eb4fca0eaefc 100644 --- a/core/util/src/jvmMain/kotlin/com/wire/kalium/util/FileUtil.kt +++ b/core/util/src/jvmMain/kotlin/com/wire/kalium/util/FileUtil.kt @@ -27,6 +27,8 @@ actual object FileUtil { return File(path).deleteRecursively() } + actual suspend fun deletePersistentDirectory(path: String): Boolean = deleteDirectory(path) + actual fun isDirectoryNonEmpty(path: String): Boolean { return File(path).listFiles()?.isNotEmpty() ?: false } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 37306f216554..e322e415032b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -39,8 +39,8 @@ pbandk = "0.15.0" turbine = "1.1.0" avs = "10.3.8" jna = "5.17.0" -core-crypto = "9.3.0" -core-crypto-kmp = "9.3.0.1-kmp" +core-crypto = "9.3.3" +core-crypto-kmp = "9.3.3.1-kmp" desugar-jdk = "2.1.3" kermit = "2.0.3" detekt = "1.23.8"