Skip to content

Commit

Permalink
Commonize
Browse files Browse the repository at this point in the history
  • Loading branch information
eygraber committed Sep 2, 2024
1 parent 7c2f77d commit bbd0d59
Show file tree
Hide file tree
Showing 103 changed files with 1,345 additions and 1,786 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ a [`WriteTransaction`] sharing that store.

```kotlin
val bill = database.transaction("customers") {
objectStore("customers").get(Key("444-44-4444")) as Customer
objectStore("customers").get(IDBKey("444-44-4444")) as Customer
}
assertEquals("Bill", bill.name)
```
Expand Down
21 changes: 11 additions & 10 deletions core/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask

plugins {
kotlin("multiplatform")
Expand All @@ -23,28 +24,28 @@ kotlin {

sourceSets {
commonMain.dependencies {
api(project(":external"))

api(libs.coroutines.core)
}

commonTest.dependencies {
implementation(kotlin("test-common"))
implementation(kotlin("test-annotations-common"))
}

jsMain.dependencies {
implementation(project(":external"))
implementation(kotlin("test"))
implementation(libs.coroutines.test)
}

jsTest.dependencies {
implementation(kotlin("test-js"))
}

wasmJsMain.dependencies {
implementation(project(":external"))
}

wasmJsTest.dependencies {
implementation(kotlin("test-wasm-js"))
}
}
}

tasks.withType<KotlinCompilationTask<*>>().configureEach {
compilerOptions {
freeCompilerArgs.add("-Xexpect-actual-classes")
}
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
package com.juul.indexeddb

import com.juul.indexeddb.external.IDBCursor
import com.juul.indexeddb.external.IDBCursorWithValue
import com.juul.indexeddb.external.IDBKey
import com.juul.indexeddb.external.JsAny
import kotlinx.coroutines.channels.SendChannel

public open class Cursor internal constructor(
internal open val cursor: IDBCursor,
private val channel: SendChannel<*>,
) {
public val key: JsAny
public val key: IDBKey
get() = cursor.key

public val primaryKey: JsAny
public val primaryKey: IDBKey
get() = cursor.primaryKey

public fun close() {
Expand All @@ -26,12 +26,12 @@ public open class Cursor internal constructor(
cursor.advance(count)
}

public fun `continue`(key: Key) {
cursor.`continue`(key.toJs())
public fun `continue`(key: IDBKey) {
cursor.`continue`(key)
}

public fun continuePrimaryKey(key: Key, primaryKey: Key) {
cursor.continuePrimaryKey(key.toJs(), primaryKey.toJs())
public fun continuePrimaryKey(key: IDBKey, primaryKey: IDBKey) {
cursor.continuePrimaryKey(key, primaryKey)
}

public enum class Direction(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.juul.indexeddb

import com.juul.indexeddb.external.IDBCursor
import com.juul.indexeddb.external.IDBKey

public sealed class CursorStart {

Expand All @@ -15,19 +14,19 @@ public sealed class CursorStart {
}

public data class Continue(
val key: Key,
val key: IDBKey,
) : CursorStart() {
override fun apply(cursor: IDBCursor) {
cursor.`continue`(key.toJs())
cursor.`continue`(key)
}
}

public data class ContinuePrimaryKey(
val key: Key,
val primaryKey: Key,
val key: IDBKey,
val primaryKey: IDBKey,
) : CursorStart() {
override fun apply(cursor: IDBCursor) {
cursor.continuePrimaryKey(key.toJs(), primaryKey.toJs())
cursor.continuePrimaryKey(key, primaryKey)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
package com.juul.indexeddb

import com.juul.indexeddb.external.IDBDatabase
import com.juul.indexeddb.external.IDBFactory
import com.juul.indexeddb.external.IDBTransactionDurability
import com.juul.indexeddb.external.IDBTransactionOptions
import com.juul.indexeddb.external.IDBVersionChangeEvent
import com.juul.indexeddb.external.indexedDB
import kotlinx.browser.window
import com.juul.indexeddb.external.window
import com.juul.indexeddb.selfIndexedDB
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext

Expand Down Expand Up @@ -81,23 +80,28 @@ public class Database internal constructor(
* - `suspend` functions composed entirely of other legal functions
*/
public suspend fun <T> transaction(
vararg store: String,
store: String,
vararg moreStores: String,
durability: IDBTransactionDurability = IDBTransactionDurability.Default,
action: suspend Transaction.() -> T,
): T = withContext(Dispatchers.Unconfined) {
check(store.isNotEmpty()) {
"At least one store needs to be passed to transaction"
}
val transaction = when {
moreStores.isEmpty() -> Transaction(
ensureDatabase().transaction(
storeName = store,
mode = "readonly",
options = IDBTransactionOptions(durability),
),
)

val transaction = Transaction(
ensureDatabase().transaction(
storeNames = ReadonlyArray(
*store.map { it.toJsString() }.toTypedArray(),
else -> Transaction(
ensureDatabase().transaction(
storeNames = toReadonlyArray(store, *moreStores),
mode = "readonly",
options = IDBTransactionOptions(durability),
),
mode = "readonly",
options = IDBTransactionOptions(durability),
),
)
)
}
val result = transaction.action()
transaction.awaitCompletion()
result
Expand All @@ -110,28 +114,34 @@ public class Database internal constructor(
* - `suspend` functions composed entirely of other legal functions
*/
public suspend fun <T> writeTransaction(
vararg store: String,
store: String,
vararg moreStores: String,
durability: IDBTransactionDurability = IDBTransactionDurability.Default,
action: suspend WriteTransaction.() -> T,
): T = withContext(Dispatchers.Unconfined) {
check(store.isNotEmpty()) {
"At least one store needs to be passed to writeTransaction"
}
val transaction = when {
moreStores.isEmpty() -> WriteTransaction(
ensureDatabase()
.transaction(
storeName = store,
mode = "readwrite",
options = IDBTransactionOptions(durability),
),
)

val transaction = WriteTransaction(
ensureDatabase()
.transaction(
storeNames = ReadonlyArray(
*store.map { it.toJsString() }.toTypedArray(),
else -> WriteTransaction(
ensureDatabase()
.transaction(
storeNames = toReadonlyArray(store, *moreStores),
mode = "readwrite",
options = IDBTransactionOptions(durability),
),
mode = "readwrite",
options = IDBTransactionOptions(durability),
),
)
)
}

with(transaction) {
// Force overlapping transactions to not call `action` until prior transactions complete.
objectStore(store.first())
objectStore(store)
.openKeyCursor(autoContinue = false)
.collect { it.close() }
}
Expand All @@ -145,6 +155,3 @@ public class Database internal constructor(
database = null
}
}

@Suppress("RedundantNullableReturnType")
private val selfIndexedDB: IDBFactory? = js("self.indexedDB || self.webkitIndexedDB")
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
package com.juul.indexeddb

import org.w3c.dom.events.Event
import com.juul.indexeddb.external.Event

public abstract class EventException(
message: String?,
Expand Down
31 changes: 31 additions & 0 deletions core/src/commonMain/kotlin/Index.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import com.juul.indexeddb.external.IDBCursor
import com.juul.indexeddb.external.IDBCursorWithValue
import com.juul.indexeddb.external.IDBIndex
import com.juul.indexeddb.external.IDBKey
import com.juul.indexeddb.external.JsNumber
import com.juul.indexeddb.external.ReadonlyArray

public class Index internal constructor(
internal val index: IDBIndex,
) : Queryable() {
override fun requestGet(key: IDBKey): Request<*> =
Request(index.get(key))

override fun requestGetAll(query: IDBKey?): Request<ReadonlyArray<*>> =
Request(index.getAll(query))

override fun requestOpenCursor(
query: IDBKey?,
direction: Cursor.Direction,
): Request<IDBCursorWithValue?> =
Request(index.openCursor(query, direction.constant))

override fun requestOpenKeyCursor(
query: IDBKey?,
direction: Cursor.Direction,
): Request<IDBCursor?> =
Request(index.openKeyCursor(query, direction.constant))

override fun requestCount(query: IDBKey?): Request<JsNumber> =
Request(index.count(query))
}
30 changes: 30 additions & 0 deletions core/src/commonMain/kotlin/JsArray.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import com.juul.indexeddb.external.JsAny
import com.juul.indexeddb.external.JsArray
import com.juul.indexeddb.external.JsString
import com.juul.indexeddb.external.ReadonlyArray
import com.juul.indexeddb.external.set
import com.juul.indexeddb.external.toJsString
import com.juul.indexeddb.toReadonlyArray

internal fun toReadonlyArray(value: String, vararg moreValues: String): ReadonlyArray<JsString> =
JsArray<JsString>()
.apply {
set(0, value.toJsString())
moreValues.forEachIndexed { index, s ->
set(index + 1, s.toJsString())
}
}.toReadonlyArray()

internal fun Iterable<String?>.toJsArray(): JsArray<JsString?> =
JsArray<JsString?>().apply {
forEachIndexed { index, s ->
set(index, s?.toJsString())
}
}

internal fun <T : JsAny?> JsArray(vararg values: T): JsArray<T> =
JsArray<T>().apply {
for (i in values.indices) {
set(i, values[i])
}
}
9 changes: 9 additions & 0 deletions core/src/commonMain/kotlin/Jso.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.juul.indexeddb

import com.juul.indexeddb.external.JsAny

// Copied from:
// https://github.com/JetBrains/kotlin-wrappers/blob/91b2c1568ec6f779af5ec10d89b5e2cbdfe785ff/kotlin-extensions/src/main/kotlin/kotlinx/js/jso.kt

internal expect fun <T : JsAny> jso(): T
internal fun <T : JsAny> jso(block: T.() -> Unit): T = jso<T>().apply(block)
39 changes: 39 additions & 0 deletions core/src/commonMain/kotlin/Key.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import com.juul.indexeddb.external.IDBKey
import com.juul.indexeddb.external.IDBKeyRange
import com.juul.indexeddb.external.JsAny
import com.juul.indexeddb.external.toJsString

public object AutoIncrement

public class KeyPath private constructor(
private val paths: List<String?>,
) {
init {
require(paths.isNotEmpty()) { "A key path must have at least one member." }
}

public constructor(path: String?, vararg morePaths: String?) : this(listOf(path, *morePaths))

internal fun toJs(): JsAny? = if (paths.size == 1) paths[0]?.toJsString() else paths.toJsArray()
}

public fun lowerBound(
x: JsAny?,
open: Boolean = false,
): IDBKey = IDBKey(IDBKeyRange.lowerBound(x, open))

public fun upperBound(
y: JsAny?,
open: Boolean = false,
): IDBKey = IDBKey(IDBKeyRange.upperBound(y, open))

public fun bound(
x: JsAny?,
y: JsAny?,
lowerOpen: Boolean = false,
upperOpen: Boolean = false,
): IDBKey = IDBKey(IDBKeyRange.bound(x, y, lowerOpen, upperOpen))

public fun only(
z: JsAny?,
): IDBKey = IDBKey(IDBKeyRange.only(z))
25 changes: 25 additions & 0 deletions core/src/commonMain/kotlin/ObjectStore.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import com.juul.indexeddb.external.IDBCursor
import com.juul.indexeddb.external.IDBCursorWithValue
import com.juul.indexeddb.external.IDBKey
import com.juul.indexeddb.external.IDBObjectStore
import com.juul.indexeddb.external.JsNumber
import com.juul.indexeddb.external.ReadonlyArray

public class ObjectStore internal constructor(
internal val objectStore: IDBObjectStore,
) : Queryable() {
override fun requestGet(key: IDBKey): Request<*> =
Request(objectStore.get(key))

override fun requestGetAll(query: IDBKey?): Request<ReadonlyArray<*>> =
Request(objectStore.getAll(query))

override fun requestOpenCursor(query: IDBKey?, direction: Cursor.Direction): Request<IDBCursorWithValue?> =
Request(objectStore.openCursor(query, direction.constant))

override fun requestOpenKeyCursor(query: IDBKey?, direction: Cursor.Direction): Request<IDBCursor?> =
Request(objectStore.openKeyCursor(query, direction.constant))

override fun requestCount(query: IDBKey?): Request<JsNumber> =
Request(objectStore.count(query))
}
Loading

0 comments on commit bbd0d59

Please sign in to comment.