@@ -4,9 +4,13 @@ import com.juul.indexeddb.external.IDBDatabase
44import com.juul.indexeddb.external.IDBFactory
55import com.juul.indexeddb.external.IDBVersionChangeEvent
66import com.juul.indexeddb.external.indexedDB
7+ import com.juul.indexeddb.logs.Logger
8+ import com.juul.indexeddb.logs.NoOpLogger
9+ import com.juul.indexeddb.logs.Type
710import kotlinx.browser.window
811import kotlinx.coroutines.Dispatchers
912import kotlinx.coroutines.withContext
13+ import org.w3c.dom.events.Event
1014
1115/* *
1216 * Inside the [initialize] block, you must not call any `suspend` functions except for:
@@ -17,6 +21,7 @@ import kotlinx.coroutines.withContext
1721public suspend fun openDatabase (
1822 name : String ,
1923 version : Int ,
24+ logger : Logger = NoOpLogger ,
2025 initialize : suspend VersionChangeTransaction .(
2126 database: Database ,
2227 oldVersion: Int ,
@@ -25,6 +30,7 @@ public suspend fun openDatabase(
2530): Database = withContext(Dispatchers .Unconfined ) {
2631 val indexedDB: IDBFactory ? = js(" self.indexedDB || self.webkitIndexedDB" ) as ? IDBFactory
2732 val factory = checkNotNull(indexedDB) { " Your browser doesn't support IndexedDB." }
33+ logger.log(Type .Database ) { " Opening database `$name ` at version `$version `" }
2834 val request = factory.open(name, version)
2935 val versionChangeEvent = request.onNextEvent(" success" , " upgradeneeded" , " error" , " blocked" ) { event ->
3036 when (event.type) {
@@ -34,36 +40,59 @@ public suspend fun openDatabase(
3440 else -> null
3541 }
3642 }
37- Database (request.result).also { database ->
43+ Database (request.result, logger ).also { database ->
3844 if (versionChangeEvent != null ) {
39- val transaction = VersionChangeTransaction (checkNotNull(request.transaction))
45+ logger.log(Type .Database , versionChangeEvent) {
46+ " Upgrading database `$name ` from version `${versionChangeEvent.oldVersion} ` to `${versionChangeEvent.newVersion} `"
47+ }
48+ val id = database.transactionId++
49+ logger.log(Type .Transaction ) { " Opening versionchange transaction $id on database `$name `" }
50+ val transaction = VersionChangeTransaction (checkNotNull(request.transaction), logger, id)
4051 transaction.initialize(database, versionChangeEvent.oldVersion, versionChangeEvent.newVersion)
41- transaction.awaitCompletion()
52+ transaction.awaitCompletion { event ->
53+ logger.log(Type .Transaction , event) { " Closed versionchange transaction $id on database `$name `" }
54+ }
4255 }
56+ logger.log(Type .Database ) { " Opened database `$name `" }
4357 }
4458}
4559
46- public suspend fun deleteDatabase (name : String ) {
60+ public suspend fun deleteDatabase (
61+ name : String ,
62+ logger : Logger = NoOpLogger ,
63+ ) {
64+ logger.log(Type .Database ) { " Deleting database `$name `" }
4765 val factory = checkNotNull(window.indexedDB) { " Your browser doesn't support IndexedDB." }
4866 val request = factory.deleteDatabase(name)
4967 request.onNextEvent(" success" , " error" , " blocked" ) { event ->
5068 when (event.type) {
51- " error" , " blocked" -> throw ErrorEventException (event)
52- else -> null
69+ " error" , " blocked" -> {
70+ logger.log(Type .Database , event) { " Delete failed for database `$name `" }
71+ throw ErrorEventException (event)
72+ }
73+
74+ else -> logger.log(Type .Database , event) { " Deleted database `$name `" }
5375 }
5476 }
5577}
5678
5779public class Database internal constructor(
5880 database : IDBDatabase ,
81+ private val logger : Logger ,
5982) {
83+ private val name = database.name
6084 private var database: IDBDatabase ? = database
85+ internal var transactionId = 0L
6186
6287 init {
88+ val callback = { event: Event ->
89+ logger.log(Type .Database , event) { " Closing database `$name ` due to event" }
90+ tryClose()
91+ }
6392 // listen for database structure changes (e.g., upgradeneeded while DB is open or deleteDatabase)
64- database.addEventListener(" versionchange" , { close() } )
93+ database.addEventListener(" versionchange" , callback )
6594 // listen for force close, e.g., browser profile on a USB drive that's ejected or db deleted through dev tools
66- database.addEventListener(" close" , { close() } )
95+ database.addEventListener(" close" , callback )
6796 }
6897
6998 internal fun ensureDatabase (): IDBDatabase = checkNotNull(database) { " database is closed" }
@@ -79,12 +108,21 @@ public class Database internal constructor(
79108 durability : Durability = Durability .Default ,
80109 action : suspend Transaction .() -> T ,
81110 ): T = withContext(Dispatchers .Unconfined ) {
111+ val id = transactionId++
112+ logger.log(Type .Transaction ) {
113+ " Opened readonly transaction $id using stores ${store.joinToString { " `$it `" }} on database `$name `"
114+ }
115+
82116 val transaction = Transaction (
83117 ensureDatabase().transaction(arrayOf(* store), " readonly" , transactionOptions(durability)),
118+ logger,
119+ id,
84120 )
85121 val result = transaction.action()
86122 transaction.commit()
87- transaction.awaitCompletion()
123+ transaction.awaitCompletion { event ->
124+ logger.log(Type .Transaction , event) { " Closed readonly transaction $id on database `$name `" }
125+ }
88126 result
89127 }
90128
@@ -99,19 +137,26 @@ public class Database internal constructor(
99137 durability : Durability = Durability .Default ,
100138 action : suspend WriteTransaction .() -> T ,
101139 ): T = withContext(Dispatchers .Unconfined ) {
140+ val id = transactionId++
141+ logger.log(Type .Transaction ) {
142+ " Opening readwrite transaction $id using stores ${store.joinToString { " `$it `" }} on database `$name `"
143+ }
144+
102145 val transaction = WriteTransaction (
103146 ensureDatabase().transaction(arrayOf(* store), " readwrite" , transactionOptions(durability)),
147+ logger,
148+ id,
104149 )
105150 with (transaction) {
106151 // Force overlapping transactions to not call `action` until prior transactions complete.
107- objectStore(store.first())
108- .openKeyCursor(autoContinue = false )
109- .collect { it.close() }
152+ objectStore(store.first()).awaitTransaction()
110153 }
111154 try {
112155 val result = transaction.action()
113156 transaction.commit()
114- transaction.awaitCompletion()
157+ transaction.awaitCompletion { event ->
158+ logger.log(Type .Transaction , event) { " Closed readwrite transaction $id on database `$name `" }
159+ }
115160 result
116161 } catch (e: Throwable ) {
117162 transaction.abort()
@@ -121,8 +166,19 @@ public class Database internal constructor(
121166 }
122167
123168 public fun close () {
124- database?.close()
125- database = null
169+ logger.log(Type .Database ) { " Closing database `$name ` due to explicit `close()`" }
170+ tryClose()
171+ }
172+
173+ private fun tryClose () {
174+ val db = database
175+ if (db != null ) {
176+ db.close()
177+ database = null
178+ logger.log(Type .Database ) { " Closed database `$name `" }
179+ } else {
180+ logger.log(Type .Database ) { " Close skipped, database `$name ` already closed" }
181+ }
126182 }
127183}
128184
0 commit comments