diff --git a/core/build.gradle b/core/build.gradle index 16d8815d7..96deb0782 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -53,6 +53,7 @@ kotlin { commonMain { dependencies { implementation kotlin('stdlib-common') + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core-common:1.2.0-alpha-2' } } @@ -66,6 +67,7 @@ kotlin { jvmMain { dependencies { implementation kotlin('stdlib-jdk8') + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.2.0-alpha-2' } } @@ -80,6 +82,7 @@ kotlin { jsMain { dependencies { implementation kotlin('stdlib-js') + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core-js:1.2.0-alpha-2' } } diff --git a/core/src/commonMain/kotlin/org/isoron/platform/io/Database.kt b/core/src/commonMain/kotlin/org/isoron/platform/io/Database.kt index 047ec370c..7b6c33a77 100644 --- a/core/src/commonMain/kotlin/org/isoron/platform/io/Database.kt +++ b/core/src/commonMain/kotlin/org/isoron/platform/io/Database.kt @@ -80,7 +80,7 @@ fun Database.getVersion() = queryInt("pragma user_version") fun Database.setVersion(v: Int) = run("pragma user_version = $v") -fun Database.migrateTo(newVersion: Int, fileOpener: FileOpener, log: Log) { +suspend fun Database.migrateTo(newVersion: Int, fileOpener: FileOpener, log: Log) { val currentVersion = getVersion() log.debug("Database", "Current database version: $currentVersion") @@ -92,9 +92,11 @@ fun Database.migrateTo(newVersion: Int, fileOpener: FileOpener, log: Log) { begin() for (v in (currentVersion + 1)..newVersion) { - val filename = sprintf("migrations/%03d.sql", v) + val sv = if(v < 10) "00$v" else if (v<100) "0$v" else "$v" + val filename = "migrations/$sv.sql" + println(filename) val migrationFile = fileOpener.openResourceFile(filename) - for (line in migrationFile.readLines()) { + for (line in migrationFile.lines()) { if (line.isEmpty()) continue run(line) } diff --git a/core/src/commonMain/kotlin/org/isoron/platform/io/Files.kt b/core/src/commonMain/kotlin/org/isoron/platform/io/Files.kt index 5be53ee5e..71a038cd5 100644 --- a/core/src/commonMain/kotlin/org/isoron/platform/io/Files.kt +++ b/core/src/commonMain/kotlin/org/isoron/platform/io/Files.kt @@ -56,6 +56,6 @@ interface UserFile { * files or translations. These files cannot be deleted. */ interface ResourceFile { - fun readLines(): List fun copyTo(dest: UserFile) + suspend fun lines(): List } \ No newline at end of file diff --git a/core/src/commonMain/kotlin/org/isoron/platform/io/Log.kt b/core/src/commonMain/kotlin/org/isoron/platform/io/Log.kt index cc59eb6c9..e8c0a10d3 100644 --- a/core/src/commonMain/kotlin/org/isoron/platform/io/Log.kt +++ b/core/src/commonMain/kotlin/org/isoron/platform/io/Log.kt @@ -29,11 +29,11 @@ interface Log { */ class StandardLog : Log { override fun info(tag: String, msg: String) { - println(sprintf("I/%-20s %s", tag, msg)) + println("I/$tag $msg") } override fun debug(tag: String, msg: String) { - println(sprintf("D/%-20s %s", tag, msg)) + println("D/$tag $msg") } } \ No newline at end of file diff --git a/core/src/commonMain/kotlin/org/isoron/uhabits/backend/Backend.kt b/core/src/commonMain/kotlin/org/isoron/uhabits/backend/Backend.kt index a81fb37f3..5b6142162 100644 --- a/core/src/commonMain/kotlin/org/isoron/uhabits/backend/Backend.kt +++ b/core/src/commonMain/kotlin/org/isoron/uhabits/backend/Backend.kt @@ -26,82 +26,82 @@ import org.isoron.uhabits.components.* import org.isoron.uhabits.i18n.* import org.isoron.uhabits.models.* -class Backend(databaseName: String, - databaseOpener: DatabaseOpener, - fileOpener: FileOpener, - localeHelper: LocaleHelper, - val log: Log, - val taskRunner: TaskRunner) { - - val database: Database - - val habitsRepository: HabitRepository - - val checkmarkRepository: CheckmarkRepository - - val habits = mutableMapOf() - - val checkmarks = mutableMapOf() - - val scores = mutableMapOf() - - val mainScreenDataSource: MainScreenDataSource - - val strings = localeHelper.getStringsForCurrentLocale() - - val preferences: Preferences - - var theme: Theme = LightTheme() - - init { - val dbFile = fileOpener.openUserFile(databaseName) - if (!dbFile.exists()) { - val templateFile = fileOpener.openResourceFile("databases/template.db") - templateFile.copyTo(dbFile) - } - database = databaseOpener.open(dbFile) - database.migrateTo(LOOP_DATABASE_VERSION, fileOpener, log) - preferences = Preferences(PreferencesRepository(database)) - habitsRepository = HabitRepository(database) - checkmarkRepository = CheckmarkRepository(database) - taskRunner.runInBackground { - habits.putAll(habitsRepository.findAll()) - for ((key, habit) in habits) { - val checks = checkmarkRepository.findAll(key) - checkmarks[habit] = CheckmarkList(habit.frequency, habit.type) - checkmarks[habit]?.setManualCheckmarks(checks) - scores[habit] = ScoreList(checkmarks[habit]!!) - } - } - mainScreenDataSource = MainScreenDataSource(preferences, - habits, - checkmarks, - scores, - taskRunner) - } - - fun createHabit(habit: Habit) { - val id = habitsRepository.nextId() - habit.id = id - habit.position = habits.size - habits[id] = habit - checkmarks[habit] = CheckmarkList(habit.frequency, habit.type) - habitsRepository.insert(habit) - mainScreenDataSource.requestData() - } - - fun deleteHabit(id: Int) { - habits[id]?.let { habit -> - habitsRepository.delete(habit) - habits.remove(id) - mainScreenDataSource.requestData() - } - } - - fun updateHabit(modified: Habit) { - habits[modified.id]?.let { existing -> - modified.position = existing.position - habitsRepository.update(modified) - } - } -} +//class Backend(databaseName: String, +// databaseOpener: DatabaseOpener, +// fileOpener: FileOpener, +// localeHelper: LocaleHelper, +// val log: Log, +// val taskRunner: TaskRunner) { +// +// val database: Database +// +// val habitsRepository: HabitRepository +// +// val checkmarkRepository: CheckmarkRepository +// +// val habits = mutableMapOf() +// +// val checkmarks = mutableMapOf() +// +// val scores = mutableMapOf() +// +// val mainScreenDataSource: MainScreenDataSource +// +// val strings = localeHelper.getStringsForCurrentLocale() +// +// val preferences: Preferences +// +// var theme: Theme = LightTheme() +// +// init { +// val dbFile = fileOpener.openUserFile(databaseName) +// if (!dbFile.exists()) { +// val templateFile = fileOpener.openResourceFile("databases/template.db") +// templateFile.copyTo(dbFile) +// } +// database = databaseOpener.open(dbFile) +// database.migrateTo(LOOP_DATABASE_VERSION, fileOpener, log) +// preferences = Preferences(PreferencesRepository(database)) +// habitsRepository = HabitRepository(database) +// checkmarkRepository = CheckmarkRepository(database) +// taskRunner.runInBackground { +// habits.putAll(habitsRepository.findAll()) +// for ((key, habit) in habits) { +// val checks = checkmarkRepository.findAll(key) +// checkmarks[habit] = CheckmarkList(habit.frequency, habit.type) +// checkmarks[habit]?.setManualCheckmarks(checks) +// scores[habit] = ScoreList(checkmarks[habit]!!) +// } +// } +// mainScreenDataSource = MainScreenDataSource(preferences, +// habits, +// checkmarks, +// scores, +// taskRunner) +// } +// +// fun createHabit(habit: Habit) { +// val id = habitsRepository.nextId() +// habit.id = id +// habit.position = habits.size +// habits[id] = habit +// checkmarks[habit] = CheckmarkList(habit.frequency, habit.type) +// habitsRepository.insert(habit) +// mainScreenDataSource.requestData() +// } +// +// fun deleteHabit(id: Int) { +// habits[id]?.let { habit -> +// habitsRepository.delete(habit) +// habits.remove(id) +// mainScreenDataSource.requestData() +// } +// } +// +// fun updateHabit(modified: Habit) { +// habits[modified.id]?.let { existing -> +// modified.position = existing.position +// habitsRepository.update(modified) +// } +// } +//} diff --git a/core/src/commonTest/kotlin/org/isoron/BaseTest.kt b/core/src/commonTest/kotlin/org/isoron/BaseTest.kt index e2b608bac..6ad312e82 100644 --- a/core/src/commonTest/kotlin/org/isoron/BaseTest.kt +++ b/core/src/commonTest/kotlin/org/isoron/BaseTest.kt @@ -22,5 +22,4 @@ package org.isoron open class BaseTest { val resolver = DependencyResolver() val fileOpener = resolver.getFileOpener() - val db = resolver.getDatabase() } \ No newline at end of file diff --git a/core/src/commonTest/kotlin/org/isoron/DependencyResolver.kt b/core/src/commonTest/kotlin/org/isoron/DependencyResolver.kt index ae521f931..739bebacf 100644 --- a/core/src/commonTest/kotlin/org/isoron/DependencyResolver.kt +++ b/core/src/commonTest/kotlin/org/isoron/DependencyResolver.kt @@ -24,7 +24,7 @@ import org.isoron.platform.io.* expect class DependencyResolver() { fun getFileOpener(): FileOpener - fun getDatabase(): Database + suspend fun getDatabase(): Database fun createCanvas(width: Int, height: Int): Canvas fun exportCanvas(canvas: Canvas, filename: String) } \ No newline at end of file diff --git a/core/src/commonTest/kotlin/org/isoron/platform/io/DatabaseTest.kt b/core/src/commonTest/kotlin/org/isoron/platform/io/DatabaseTest.kt index e3849f0b1..a9101995b 100644 --- a/core/src/commonTest/kotlin/org/isoron/platform/io/DatabaseTest.kt +++ b/core/src/commonTest/kotlin/org/isoron/platform/io/DatabaseTest.kt @@ -23,8 +23,8 @@ import org.isoron.* import kotlin.test.* class DatabaseTest() : BaseTest() { - @Test - fun testUsage() { + suspend fun testUsage() { + val db = resolver.getDatabase() db.setVersion(0) assertEquals(0, db.getVersion()) diff --git a/core/src/commonTest/kotlin/org/isoron/platform/io/FilesTest.kt b/core/src/commonTest/kotlin/org/isoron/platform/io/FilesTest.kt index 7b3e5c01a..6532f4b09 100644 --- a/core/src/commonTest/kotlin/org/isoron/platform/io/FilesTest.kt +++ b/core/src/commonTest/kotlin/org/isoron/platform/io/FilesTest.kt @@ -23,15 +23,14 @@ import org.isoron.* import kotlin.test.* class FilesTest() : BaseTest() { - @Test - fun testReadLines() { + suspend fun testLines() { val hello = fileOpener.openResourceFile("hello.txt") - var lines = hello.readLines() + var lines = hello.lines() assertEquals("Hello World!", lines[0]) assertEquals("This is a resource.", lines[1]) val migration = fileOpener.openResourceFile("migrations/012.sql") - lines = migration.readLines() + lines = migration.lines() assertEquals("delete from Score", lines[0]) } } \ No newline at end of file diff --git a/core/src/commonTest/kotlin/org/isoron/uhabits/models/CheckmarkRepositoryTest.kt b/core/src/commonTest/kotlin/org/isoron/uhabits/models/CheckmarkRepositoryTest.kt index 8e225e260..57f9d94ea 100644 --- a/core/src/commonTest/kotlin/org/isoron/uhabits/models/CheckmarkRepositoryTest.kt +++ b/core/src/commonTest/kotlin/org/isoron/uhabits/models/CheckmarkRepositoryTest.kt @@ -25,8 +25,8 @@ import kotlin.test.* class CheckmarkRepositoryTest : BaseTest() { - @Test - fun testCRUD() { + suspend fun testCRUD() { + val db = resolver.getDatabase() val habitA = 10 var checkmarksA = listOf(Checkmark(LocalDate(2019, 1, 15), 100), Checkmark(LocalDate(2019, 1, 7), 500), diff --git a/core/src/commonTest/kotlin/org/isoron/uhabits/models/HabitRepositoryTest.kt b/core/src/commonTest/kotlin/org/isoron/uhabits/models/HabitRepositoryTest.kt index ab733c790..09060b189 100644 --- a/core/src/commonTest/kotlin/org/isoron/uhabits/models/HabitRepositoryTest.kt +++ b/core/src/commonTest/kotlin/org/isoron/uhabits/models/HabitRepositoryTest.kt @@ -25,52 +25,43 @@ import org.isoron.platform.io.* import kotlin.test.* class HabitRepositoryTest() : BaseTest() { + suspend fun testCRUD() { + val db = resolver.getDatabase() + val original0 = Habit(id = 0, + name = "Wake up early", + description = "Did you wake up before 6am?", + frequency = Frequency(1, 1), + color = PaletteColor(3), + isArchived = false, + position = 0, + unit = "", + target = 0.0, + type = HabitType.BOOLEAN_HABIT) - lateinit var repository: HabitRepository - lateinit private var original0: Habit - lateinit private var original1: Habit - lateinit private var original2: Habit + val original1 = Habit(id = 1, + name = "Exercise", + description = "Did you exercise for at least 20 minutes?", + frequency = Frequency(1, 2), + color = PaletteColor(4), + isArchived = false, + position = 1, + unit = "", + target = 0.0, + type = HabitType.BOOLEAN_HABIT) - @BeforeTest - fun setUp() { - original0 = Habit(id = 0, - name = "Wake up early", - description = "Did you wake up before 6am?", - frequency = Frequency(1, 1), - color = PaletteColor(3), - isArchived = false, - position = 0, - unit = "", - target = 0.0, - type = HabitType.BOOLEAN_HABIT) + val original2 = Habit(id = 2, + name = "Learn Japanese", + description = "Did you study Japanese today?", + frequency = Frequency(1, 1), + color = PaletteColor(3), + isArchived = false, + position = 2, + unit = "", + target = 0.0, + type = HabitType.BOOLEAN_HABIT) - original1 = Habit(id = 1, - name = "Exercise", - description = "Did you exercise for at least 20 minutes?", - frequency = Frequency(1, 2), - color = PaletteColor(4), - isArchived = false, - position = 1, - unit = "", - target = 0.0, - type = HabitType.BOOLEAN_HABIT) + val repository = HabitRepository(db) - original2 = Habit(id = 2, - name = "Learn Japanese", - description = "Did you study Japanese today?", - frequency = Frequency(1, 1), - color = PaletteColor(3), - isArchived = false, - position = 2, - unit = "", - target = 0.0, - type = HabitType.BOOLEAN_HABIT) - - repository = HabitRepository(db) - } - - @Test - fun testFindAll() { var habits = repository.findAll() assertEquals(0, repository.nextId()) assertEquals(0, habits.size) diff --git a/core/src/commonTest/kotlin/org/isoron/uhabits/models/PreferencesRepositoryTest.kt b/core/src/commonTest/kotlin/org/isoron/uhabits/models/PreferencesRepositoryTest.kt index 9f7b673ee..4a3f70839 100644 --- a/core/src/commonTest/kotlin/org/isoron/uhabits/models/PreferencesRepositoryTest.kt +++ b/core/src/commonTest/kotlin/org/isoron/uhabits/models/PreferencesRepositoryTest.kt @@ -23,8 +23,8 @@ import org.isoron.* import kotlin.test.* class PreferencesRepositoryTest : BaseTest() { - @Test - fun testUsage() { + suspend fun testUsage() { + val db = resolver.getDatabase() val prefs = PreferencesRepository(db) assertEquals("default", prefs.getString("non_existing_key", "default")) diff --git a/core/src/jsMain/kotlin/org/isoron/platform/io/JsDatabase.kt b/core/src/jsMain/kotlin/org/isoron/platform/io/JsDatabase.kt index 850b0e0d9..89655e825 100644 --- a/core/src/jsMain/kotlin/org/isoron/platform/io/JsDatabase.kt +++ b/core/src/jsMain/kotlin/org/isoron/platform/io/JsDatabase.kt @@ -29,6 +29,7 @@ class JsPreparedStatement(val stmt: dynamic) : PreparedStatement { } override fun finalize() { + stmt.free() } override fun getInt(index: Int): Int { diff --git a/core/src/jsMain/kotlin/org/isoron/platform/io/JsFiles.kt b/core/src/jsMain/kotlin/org/isoron/platform/io/JsFiles.kt index 98345e3c3..0b876d267 100644 --- a/core/src/jsMain/kotlin/org/isoron/platform/io/JsFiles.kt +++ b/core/src/jsMain/kotlin/org/isoron/platform/io/JsFiles.kt @@ -19,8 +19,10 @@ package org.isoron.platform.io +import kotlinx.coroutines.* import org.w3c.dom.events.* import org.w3c.xhr.* +import kotlin.js.* class JsFileOpener : FileOpener { override fun openUserFile(filename: String): UserFile { @@ -34,21 +36,26 @@ class JsFileOpener : FileOpener { class JsUserFile(filename: String) : UserFile { override fun delete() { + TODO() } override fun exists(): Boolean { - return false + TODO() } } class JsResourceFile(val filename: String) : ResourceFile { - override fun readLines(): List { - val xhr = XMLHttpRequest() - xhr.open("GET", "/assets/$filename", false) - xhr.send() - return xhr.responseText.lines() + override suspend fun lines(): List { + return Promise> { resolve, reject -> + val xhr = XMLHttpRequest() + xhr.open("GET", "/assets/$filename", true) + xhr.onload = { resolve(xhr.responseText.lines()) } + xhr.onerror = { reject(Exception()) } + xhr.send() + }.await() } override fun copyTo(dest: UserFile) { + TODO() } } diff --git a/core/src/jsTest/kotlin/org/isoron/DependencyResolver.kt b/core/src/jsTest/kotlin/org/isoron/DependencyResolver.kt index 56263c502..c3d456a3c 100644 --- a/core/src/jsTest/kotlin/org/isoron/DependencyResolver.kt +++ b/core/src/jsTest/kotlin/org/isoron/DependencyResolver.kt @@ -21,15 +21,18 @@ package org.isoron import org.isoron.platform.gui.* import org.isoron.platform.io.* +import org.isoron.uhabits.* import org.w3c.dom.* import kotlin.browser.* actual class DependencyResolver { actual fun getFileOpener(): FileOpener = JsFileOpener() - actual fun getDatabase(): Database { - val db = eval("new SQL.Database()") - return JsDatabase(db) + actual suspend fun getDatabase(): Database { + val nativeDB = eval("new SQL.Database()") + val db = JsDatabase(nativeDB) + db.migrateTo(LOOP_DATABASE_VERSION, getFileOpener(), StandardLog()) + return db } actual fun createCanvas(width: Int, height: Int): Canvas { diff --git a/core/src/jsTest/kotlin/org/isoron/JsAsyncTests.kt b/core/src/jsTest/kotlin/org/isoron/JsAsyncTests.kt new file mode 100644 index 000000000..f026c66ed --- /dev/null +++ b/core/src/jsTest/kotlin/org/isoron/JsAsyncTests.kt @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2016-2019 Álinson Santos Xavier + * + * This file is part of Loop Habit Tracker. + * + * Loop Habit Tracker 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. + * + * Loop Habit Tracker 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 . + */ + +package org.isoron + +import kotlinx.coroutines.* +import org.isoron.platform.io.* +import org.isoron.uhabits.models.* +import kotlin.test.* + +class JsAsyncTests { + @Test + fun testLines() = GlobalScope.promise { FilesTest().testLines() } + + @Test + fun testDatabase() = GlobalScope.promise { DatabaseTest().testUsage() } + + @Test + fun testCheckmarkRepository() = GlobalScope.promise { CheckmarkRepositoryTest().testCRUD() } + + @Test + fun testHabitRepository() = GlobalScope.promise { HabitRepositoryTest().testCRUD() } + + @Test + fun testPreferencesRepository() = GlobalScope.promise { PreferencesRepositoryTest().testUsage() } +} \ No newline at end of file diff --git a/core/src/jvmMain/kotlin/org/isoron/platform/io/JavaFiles.kt b/core/src/jvmMain/kotlin/org/isoron/platform/io/JavaFiles.kt index b87565a03..7b57feace 100644 --- a/core/src/jvmMain/kotlin/org/isoron/platform/io/JavaFiles.kt +++ b/core/src/jvmMain/kotlin/org/isoron/platform/io/JavaFiles.kt @@ -23,12 +23,12 @@ import java.io.* import java.nio.file.* class JavaResourceFile(private val path: Path) : ResourceFile { - override fun copyTo(dest: UserFile) { - Files.copy(path, (dest as JavaUserFile).path) + override suspend fun lines(): List { + return Files.readAllLines(path) } - override fun readLines(): List { - return Files.readAllLines(path) + override fun copyTo(dest: UserFile) { + Files.copy(path, (dest as JavaUserFile).path) } fun stream(): InputStream { diff --git a/core/src/jvmTest/kotlin/org/isoron/DependencyResolver.kt b/core/src/jvmTest/kotlin/org/isoron/DependencyResolver.kt index 363dd0490..c8fff12f4 100644 --- a/core/src/jvmTest/kotlin/org/isoron/DependencyResolver.kt +++ b/core/src/jvmTest/kotlin/org/isoron/DependencyResolver.kt @@ -19,6 +19,7 @@ package org.isoron +import kotlinx.coroutines.* import org.isoron.platform.gui.* import org.isoron.platform.io.* import org.isoron.uhabits.* @@ -34,7 +35,7 @@ actual class DependencyResolver actual constructor() { actual fun getFileOpener(): FileOpener = fileOpener - actual fun getDatabase(): Database { + actual suspend fun getDatabase(): Database { val dbFile = fileOpener.openUserFile("test.sqlite3") if (dbFile.exists()) dbFile.delete() val db = databaseOpener.open(dbFile) diff --git a/core/src/jvmTest/kotlin/org/isoron/JavaAsyncTests.kt b/core/src/jvmTest/kotlin/org/isoron/JavaAsyncTests.kt new file mode 100644 index 000000000..f49180335 --- /dev/null +++ b/core/src/jvmTest/kotlin/org/isoron/JavaAsyncTests.kt @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2016-2019 Álinson Santos Xavier + * + * This file is part of Loop Habit Tracker. + * + * Loop Habit Tracker 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. + * + * Loop Habit Tracker 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 . + */ + +package org.isoron + +import kotlinx.coroutines.* +import org.isoron.platform.io.* +import org.isoron.uhabits.models.* +import org.junit.* + +class JavaAsyncTests { + @Test + fun testLines() = runBlocking { FilesTest().testLines() } + + @Test + fun testDatabase() = runBlocking { DatabaseTest().testUsage() } + + @Test + fun testCheckmarkRepository() = runBlocking { CheckmarkRepositoryTest().testCRUD() } + + @Test + fun testHabitRepository() = runBlocking { HabitRepositoryTest().testCRUD() } + + @Test + fun testPreferencesRepository() = runBlocking { PreferencesRepositoryTest().testUsage() } +} \ No newline at end of file diff --git a/web/package-lock.json b/web/package-lock.json index 40498b3c8..e14133a85 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -3615,6 +3615,11 @@ "kotlin": "1.3.21" } }, + "kotlinx-coroutines-core": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/kotlinx-coroutines-core/-/kotlinx-coroutines-core-1.1.1.tgz", + "integrity": "sha512-RnxF8HVQlMmLQcmJXSZQowR9WpsoeslY6ogqDovb/2HumkkaUBlJuR4eiXwX0DDnoAq8mGPvncl1lRAhK2+ovg==" + }, "lcid": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", diff --git a/web/package.json b/web/package.json index f0734849f..05f48df4d 100644 --- a/web/package.json +++ b/web/package.json @@ -20,6 +20,7 @@ "babel-preset-react": "^6.24.1", "kotlin": "^1.3.21", "kotlin-test": "^1.3.21", + "kotlinx-coroutines-core": "^1.1.1", "sql.js": "^0.5.0" }, "devDependencies": {