diff --git a/.github/workflows/pr-package-lock.yml b/.github/workflows/pr-package-lock.yml new file mode 100644 index 0000000..c26388f --- /dev/null +++ b/.github/workflows/pr-package-lock.yml @@ -0,0 +1,38 @@ +name: Upgrade Package Lock + +on: + pull_request + +jobs: + upgrade_package_lock: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + repository: ${{ github.event.pull_request.head.repo.full_name }} + ref: ${{ github.event.pull_request.head.ref }} + token: ${{ secrets.PUSH_PAT }} + + - uses: actions/setup-java@v4 + with: + distribution: 'zulu' + java-version-file: .ci-java-version + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + with: + gradle-version: wrapper + + - name: Run assemble task + run: ./gradlew kotlinUpgradePackageLock --rerun-tasks + + - name: Commit package lock changes + id: commit_package_lock_changes + uses: EndBug/add-and-commit@v9 + with: + add: "['kotlin-js-store/package-lock.json']" + default_author: github_actions + message: "Upgrade package lock" + +env: + GRADLE_OPTS: -Dorg.gradle.daemon=false -Dkotlin.incremental=false -Dorg.gradle.jvmargs="-Xmx16g -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 -XX:MaxMetaspaceSize=1024m" diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 45be127..4ab7b5b 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -15,7 +15,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} assemble: - runs-on: macos-latest + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -30,10 +30,10 @@ jobs: gradle-version: wrapper - name: Run assemble task - run: ./gradlew assemble -x assembleRelease -x jsBrowserProductionWebpack -x wasmJsBrowserProductionWebpack -x compileProductionExecutableKotlinWasmJs -x linkReleaseFrameworkIosArm64 -x linkReleaseFrameworkIosX64 -x linkReleaseFrameworkIosFat -x linkReleaseFrameworkIosSimulatorArm64 + run: ./gradlew assemble -x assembleRelease -x jsBrowserProductionWebpack -x wasmJsBrowserProductionWebpack -x compileProductionExecutableKotlinWasmJs - detekt: - runs-on: ubuntu-latest + assemble-ios-frameworks: + runs-on: macos-latest steps: - uses: actions/checkout@v4 @@ -47,11 +47,11 @@ jobs: with: gradle-version: wrapper - - name: Run detekt - run: ./gradlew detektAll + - name: Assemble sample ios frameworks + run: ./gradlew :samples:auth:ios-framework:assemble :samples:todo:ios-framework:assemble -x linkReleaseFrameworkIosArm64 -x linkReleaseFrameworkIosX64 -x linkReleaseFrameworkIosFat -x linkReleaseFrameworkIosSimulatorArm64 - detekt-apple: - runs-on: macos-latest + detekt: + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -65,8 +65,8 @@ jobs: with: gradle-version: wrapper - - name: Run detekt for apple targets - run: ./gradlew detektAppleMain detektAppleTest + - name: Run detekt + run: ./gradlew detektAll dependency_analysis: runs-on: ubuntu-latest @@ -113,7 +113,10 @@ jobs: run: ./gradlew lintRelease test: - runs-on: macos-latest + strategy: + matrix: + os: [ macos-latest, ubuntu-latest ] + runs-on: ${{matrix.os}} steps: - uses: actions/checkout@v4 @@ -129,6 +132,11 @@ jobs: - name: Run tests run: ./gradlew allTests + if: matrix.os == 'ubuntu-latest' + + - name: Run Apple tests + run: ./gradlew iosX64Test + if: matrix.os == 'macos-latest' env: GRADLE_OPTS: -Dorg.gradle.daemon=false -Dkotlin.incremental=false -Dorg.gradle.jvmargs="-Xmx16g -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 -XX:MaxMetaspaceSize=1024m" diff --git a/.github/workflows/publish_release.yml b/.github/workflows/publish_release.yml index d858e83..4245bea 100644 --- a/.github/workflows/publish_release.yml +++ b/.github/workflows/publish_release.yml @@ -11,7 +11,7 @@ env: jobs: publish_artifacts: - runs-on: macos-latest + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -42,28 +42,6 @@ jobs: ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.ARTIFACT_SIGNING_PRIVATE_KEY }} run: ./gradlew publishAllPublicationsToMavenCentralRepository -Pversion=${{ env.RELEASE_VERSION }} - publish_release: - needs: publish_artifacts - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - with: - token: ${{ secrets.PUSH_PAT }} - - - name: Generate versions - uses: HardNorth/github-version-generate@v1 - with: - version-source: file - version-file: ${{ env.VERSION_FILE }} - version-file-extraction-pattern: ${{ env.VERSION_EXTRACT_PATTERN }} - - - uses: actions/setup-java@v4 - with: - distribution: 'zulu' - java-version-file: .ci-java-version - - name: Create, checkout, and push release branch run: | git config user.name eygraber diff --git a/.github/workflows/publish_snapshot_release.yml b/.github/workflows/publish_snapshot_release.yml index 028e8c4..a204fb6 100644 --- a/.github/workflows/publish_snapshot_release.yml +++ b/.github/workflows/publish_snapshot_release.yml @@ -7,7 +7,7 @@ on: jobs: publish_snapshot: - runs-on: macos-latest + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 diff --git a/gradle.properties b/gradle.properties index 87fa8d8..6d11806 100644 --- a/gradle.properties +++ b/gradle.properties @@ -47,8 +47,9 @@ org.gradle.configuration-cache=false org.gradle.configureondemand=false #Kotlin +kotlin.incremental.wasm=true kotlin.js.yarn=false -kotlin.mpp.androidGradlePluginCompatibility.nowarn=true +kotlin.native.enableKlibsCrossCompilation=true kotlin.native.ignoreDisabledTargets=true kotlinx.atomicfu.enableJvmIrTransformation=true diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index cb6968d..e26aafe 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -13,7 +13,7 @@ atomicfu = "0.26.0" composeJetbrains = "1.7.1" -conventions = "0.0.79" +conventions = "0.0.81" cryptoKmp = "0.4.0" @@ -23,7 +23,7 @@ detektEygraber = "1.0.11" indexedDb = "0.0.1" -kotlin = "2.0.20" +kotlin = "2.1.0" kotlinInject = "0.7.2" @@ -31,13 +31,13 @@ kotlinx-coroutines = "1.9.0" kotlinx-serialization = "1.7.3" kotlinx-datetime = "0.6.1" -ksp = "2.0.20-1.0.25" +ksp = "2.1.0-1.0.28" kstore = "0.9.1" ktlint = "1.4.1" -vice = "0.9.4" +vice = "0.9.5" [plugins] atomicfu = { id = "org.jetbrains.kotlinx.atomicfu", version.ref = "atomicfu" } @@ -91,6 +91,7 @@ kotlinx-io = "org.jetbrains.kotlinx:kotlinx-io-core:0.6.0" kotlinx-serialization-cbor = { module = "org.jetbrains.kotlinx:kotlinx-serialization-cbor", version.ref = "kotlinx-serialization" } kotlinx-serialization-core = { module = "org.jetbrains.kotlinx:kotlinx-serialization-core", version.ref = "kotlinx-serialization" } kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinx-serialization" } +kotlinx-wasm-browser = "org.jetbrains.kotlinx:kotlinx-browser:0.3" kotlinInject-compiler = { module = "me.tatarka.inject:kotlin-inject-compiler-ksp", version.ref = "kotlinInject" } kotlinInject-runtime = { module = "me.tatarka.inject:kotlin-inject-runtime", version.ref = "kotlinInject" } diff --git a/kotlin-js-store/package-lock.json b/kotlin-js-store/package-lock.json index cba8069..583cf21 100644 --- a/kotlin-js-store/package-lock.json +++ b/kotlin-js-store/package-lock.json @@ -95,10 +95,9 @@ "packages_imported/skiko-wasm-js/0.8.18", "packages_imported/skiko-js-wasm-runtime/0.8.18", "packages_imported/Kotlin-DateTime-library-kotlinx-datetime-wasm-js/0.6.1", - "packages_imported/kotlin-test-js-runner/0.0.2", "packages_imported/skiko-js/0.8.18", "packages_imported/Kotlin-DateTime-library-kotlinx-datetime/0.6.1", - "packages_imported/vice-nav-js/0.9.4", + "packages_imported/vice-nav-js/0.9.5", "packages_imported/components-resources-js/1.7.1" ], "devDependencies": {} @@ -249,30 +248,10 @@ "@types/node": "*" } }, - "node_modules/@types/eslint": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.0.tgz", - "integrity": "sha512-gi6WQJ7cHRgZxtkQEoyHMppPjq9Kxo5Tjn2prSKDSmZrCz8TZ3jSRCeTJm+WoM+oB0WG37bRqLzaaU3q7JypGg==", - "dev": true, - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "node_modules/@types/eslint-scope": { - "version": "3.7.7", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", - "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", - "dev": true, - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, "node_modules/@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", "dev": true }, "node_modules/@types/express": { @@ -2554,9 +2533,9 @@ } }, "node_modules/karma": { - "version": "6.4.3", - "resolved": "https://registry.npmjs.org/karma/-/karma-6.4.3.tgz", - "integrity": "sha512-LuucC/RE92tJ8mlCwqEoRWXP38UMAqpnq98vktmS9SznSoUPPUJQbc91dHcxcunROvfQjdORVA/YFviH+Xci9Q==", + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/karma/-/karma-6.4.4.tgz", + "integrity": "sha512-LrtUxbdvt1gOpo3gxG+VAJlJAEMhbWlM4YrFQgql98FwF7+K8K12LYO4hnDdUkNjeztYrOXEMqgTajSWgmtI/w==", "dev": true, "dependencies": { "@colors/colors": "1.5.0", @@ -2676,9 +2655,14 @@ "resolved": "packages_imported/Kotlin-DateTime-library-kotlinx-datetime-wasm-js/0.6.1", "link": true }, - "node_modules/kotlin-test-js-runner": { - "resolved": "packages_imported/kotlin-test-js-runner/0.0.2", - "link": true + "node_modules/kotlin-web-helpers": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kotlin-web-helpers/-/kotlin-web-helpers-2.0.0.tgz", + "integrity": "sha512-xkVGl60Ygn/zuLkDPx+oHj7jeLR7hCvoNF99nhwXMn8a3ApB4lLiC9pk4ol4NHPjyoCbvQctBqvzUcp8pkqyWw==", + "dev": true, + "dependencies": { + "format-util": "^1.0.5" + } }, "node_modules/launch-editor": { "version": "2.8.1", @@ -2912,9 +2896,9 @@ } }, "node_modules/mocha": { - "version": "10.7.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.7.0.tgz", - "integrity": "sha512-v8/rBWr2VO5YkspYINnvu81inSz2y3ODJrhO175/Exzor1RcEZZkizgE2A+w/CAXXoESS8Kys5E62dOHGHzULA==", + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.7.3.tgz", + "integrity": "sha512-uQWxAu44wwiACGqjbPYmjo7Lg8sFrS3dQe7PP2FQI+woptP4vZXSMcfMyFL/e1yFEeEpV4RtyTpZROOKmxis+A==", "dev": true, "dependencies": { "ansi-colors": "^4.1.3", @@ -4526,7 +4510,7 @@ } }, "node_modules/vice-nav-js": { - "resolved": "packages_imported/vice-nav-js/0.9.4", + "resolved": "packages_imported/vice-nav-js/0.9.5", "link": true }, "node_modules/virtue-samples-auth-shared": { @@ -4865,12 +4849,11 @@ } }, "node_modules/webpack": { - "version": "5.93.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.93.0.tgz", - "integrity": "sha512-Y0m5oEY1LRuwly578VqluorkXbvXKh7U3rLoQCEO04M97ScRr44afGVkI0FQFsXzysk5OgFAxjZAb9rsGQVihA==", + "version": "5.94.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz", + "integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==", "dev": true, "dependencies": { - "@types/eslint-scope": "^3.7.3", "@types/estree": "^1.0.5", "@webassemblyjs/ast": "^1.12.1", "@webassemblyjs/wasm-edit": "^1.12.1", @@ -4879,7 +4862,7 @@ "acorn-import-attributes": "^1.9.5", "browserslist": "^4.21.10", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.17.0", + "enhanced-resolve": "^5.17.1", "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", @@ -5448,6 +5431,7 @@ "packages_imported/kotlin-test-js-runner/0.0.2": { "name": "kotlin-test-js-runner", "version": "0.0.2", + "extraneous": true, "dependencies": { "format-util": "^1.0.5" }, @@ -5549,6 +5533,18 @@ "packages_imported/vice-nav-js/0.9.4": { "name": "vice-nav-js", "version": "0.9.4", + "extraneous": true, + "devDependencies": {} + }, + "packages_imported/vice-nav-js/0.9.5": { + "name": "vice-nav-js", + "version": "0.9.5", + "devDependencies": {} + }, + "packages_imported/vice-nav-js/0.9.5-SNAPSHOT": { + "name": "vice-nav-js", + "version": "0.9.5-SNAPSHOT", + "extraneous": true, "devDependencies": {} }, "packages/auth-sample-js": { @@ -5558,9 +5554,10 @@ "format-util": "^1.0.5" }, "devDependencies": { + "kotlin-web-helpers": "2.0.0", "source-map-loader": "5.0.0", "typescript": "5.5.4", - "webpack": "5.93.0", + "webpack": "5.94.0", "webpack-cli": "5.1.4", "webpack-dev-server": "4.15.2" } @@ -5572,15 +5569,16 @@ "format-util": "^1.0.5" }, "devDependencies": { - "karma": "6.4.3", + "karma": "6.4.4", "karma-chrome-launcher": "3.2.0", "karma-mocha": "2.0.1", "karma-sourcemap-loader": "0.4.0", "karma-webpack": "5.0.1", - "mocha": "10.7.0", + "kotlin-web-helpers": "2.0.0", + "mocha": "10.7.3", "source-map-loader": "5.0.0", "typescript": "5.5.4", - "webpack": "5.93.0", + "webpack": "5.94.0", "webpack-cli": "5.1.4" } }, @@ -5591,9 +5589,10 @@ "format-util": "^1.0.5" }, "devDependencies": { + "kotlin-web-helpers": "2.0.0", "source-map-loader": "5.0.0", "typescript": "5.5.4", - "webpack": "5.93.0", + "webpack": "5.94.0", "webpack-cli": "5.1.4", "webpack-dev-server": "4.15.2" } @@ -5605,15 +5604,16 @@ "format-util": "^1.0.5" }, "devDependencies": { - "karma": "6.4.3", + "karma": "6.4.4", "karma-chrome-launcher": "3.2.0", "karma-mocha": "2.0.1", "karma-sourcemap-loader": "0.4.0", "karma-webpack": "5.0.1", - "mocha": "10.7.0", + "kotlin-web-helpers": "2.0.0", + "mocha": "10.7.3", "source-map-loader": "5.0.0", "typescript": "5.5.4", - "webpack": "5.93.0", + "webpack": "5.94.0", "webpack-cli": "5.1.4", "webpack-dev-server": "4.15.2" } @@ -5625,9 +5625,10 @@ "format-util": "^1.0.5" }, "devDependencies": { + "kotlin-web-helpers": "2.0.0", "source-map-loader": "5.0.0", "typescript": "5.5.4", - "webpack": "5.93.0", + "webpack": "5.94.0", "webpack-cli": "5.1.4", "webpack-dev-server": "4.15.2" } @@ -5639,15 +5640,16 @@ "format-util": "^1.0.5" }, "devDependencies": { - "karma": "6.4.3", + "karma": "6.4.4", "karma-chrome-launcher": "3.2.0", "karma-mocha": "2.0.1", "karma-sourcemap-loader": "0.4.0", "karma-webpack": "5.0.1", - "mocha": "10.7.0", + "kotlin-web-helpers": "2.0.0", + "mocha": "10.7.3", "source-map-loader": "5.0.0", "typescript": "5.5.4", - "webpack": "5.93.0", + "webpack": "5.94.0", "webpack-cli": "5.1.4" } }, @@ -5658,9 +5660,10 @@ "format-util": "^1.0.5" }, "devDependencies": { + "kotlin-web-helpers": "2.0.0", "source-map-loader": "5.0.0", "typescript": "5.5.4", - "webpack": "5.93.0", + "webpack": "5.94.0", "webpack-cli": "5.1.4", "webpack-dev-server": "4.15.2" } @@ -5672,15 +5675,16 @@ "format-util": "^1.0.5" }, "devDependencies": { - "karma": "6.4.3", + "karma": "6.4.4", "karma-chrome-launcher": "3.2.0", "karma-mocha": "2.0.1", "karma-sourcemap-loader": "0.4.0", "karma-webpack": "5.0.1", - "mocha": "10.7.0", + "kotlin-web-helpers": "2.0.0", + "mocha": "10.7.3", "source-map-loader": "5.0.0", "typescript": "5.5.4", - "webpack": "5.93.0", + "webpack": "5.94.0", "webpack-cli": "5.1.4", "webpack-dev-server": "4.15.2" } @@ -5700,15 +5704,16 @@ "format-util": "^1.0.5" }, "devDependencies": { - "karma": "6.4.3", + "karma": "6.4.4", "karma-chrome-launcher": "3.2.0", "karma-mocha": "2.0.1", "karma-sourcemap-loader": "0.4.0", "karma-webpack": "5.0.1", - "mocha": "10.7.0", + "kotlin-web-helpers": "2.0.0", + "mocha": "10.7.3", "source-map-loader": "5.0.0", "typescript": "5.5.4", - "webpack": "5.93.0", + "webpack": "5.94.0", "webpack-cli": "5.1.4" } }, @@ -5727,15 +5732,16 @@ "format-util": "^1.0.5" }, "devDependencies": { - "karma": "6.4.3", + "karma": "6.4.4", "karma-chrome-launcher": "3.2.0", "karma-mocha": "2.0.1", "karma-sourcemap-loader": "0.4.0", "karma-webpack": "5.0.1", - "mocha": "10.7.0", + "kotlin-web-helpers": "2.0.0", + "mocha": "10.7.3", "source-map-loader": "5.0.0", "typescript": "5.5.4", - "webpack": "5.93.0", + "webpack": "5.94.0", "webpack-cli": "5.1.4" } }, @@ -5754,15 +5760,16 @@ "format-util": "^1.0.5" }, "devDependencies": { - "karma": "6.4.3", + "karma": "6.4.4", "karma-chrome-launcher": "3.2.0", "karma-mocha": "2.0.1", "karma-sourcemap-loader": "0.4.0", "karma-webpack": "5.0.1", - "mocha": "10.7.0", + "kotlin-web-helpers": "2.0.0", + "mocha": "10.7.3", "source-map-loader": "5.0.0", "typescript": "5.5.4", - "webpack": "5.93.0", + "webpack": "5.94.0", "webpack-cli": "5.1.4" } }, @@ -5781,15 +5788,16 @@ "format-util": "^1.0.5" }, "devDependencies": { - "karma": "6.4.3", + "karma": "6.4.4", "karma-chrome-launcher": "3.2.0", "karma-mocha": "2.0.1", "karma-sourcemap-loader": "0.4.0", "karma-webpack": "5.0.1", - "mocha": "10.7.0", + "kotlin-web-helpers": "2.0.0", + "mocha": "10.7.3", "source-map-loader": "5.0.0", "typescript": "5.5.4", - "webpack": "5.93.0", + "webpack": "5.94.0", "webpack-cli": "5.1.4" } }, @@ -5808,15 +5816,16 @@ "format-util": "^1.0.5" }, "devDependencies": { - "karma": "6.4.3", + "karma": "6.4.4", "karma-chrome-launcher": "3.2.0", "karma-mocha": "2.0.1", "karma-sourcemap-loader": "0.4.0", "karma-webpack": "5.0.1", - "mocha": "10.7.0", + "kotlin-web-helpers": "2.0.0", + "mocha": "10.7.3", "source-map-loader": "5.0.0", "typescript": "5.5.4", - "webpack": "5.93.0", + "webpack": "5.94.0", "webpack-cli": "5.1.4" } }, @@ -5835,15 +5844,16 @@ "format-util": "^1.0.5" }, "devDependencies": { - "karma": "6.4.3", + "karma": "6.4.4", "karma-chrome-launcher": "3.2.0", "karma-mocha": "2.0.1", "karma-sourcemap-loader": "0.4.0", "karma-webpack": "5.0.1", - "mocha": "10.7.0", + "kotlin-web-helpers": "2.0.0", + "mocha": "10.7.3", "source-map-loader": "5.0.0", "typescript": "5.5.4", - "webpack": "5.93.0", + "webpack": "5.94.0", "webpack-cli": "5.1.4" } }, @@ -5862,15 +5872,16 @@ "format-util": "^1.0.5" }, "devDependencies": { - "karma": "6.4.3", + "karma": "6.4.4", "karma-chrome-launcher": "3.2.0", "karma-mocha": "2.0.1", "karma-sourcemap-loader": "0.4.0", "karma-webpack": "5.0.1", - "mocha": "10.7.0", + "kotlin-web-helpers": "2.0.0", + "mocha": "10.7.3", "source-map-loader": "5.0.0", "typescript": "5.5.4", - "webpack": "5.93.0", + "webpack": "5.94.0", "webpack-cli": "5.1.4" } }, @@ -5889,465 +5900,376 @@ "format-util": "^1.0.5" }, "devDependencies": { - "karma": "6.4.3", + "karma": "6.4.4", "karma-chrome-launcher": "3.2.0", "karma-mocha": "2.0.1", "karma-sourcemap-loader": "0.4.0", "karma-webpack": "5.0.1", - "mocha": "10.7.0", + "kotlin-web-helpers": "2.0.0", + "mocha": "10.7.3", "source-map-loader": "5.0.0", "typescript": "5.5.4", - "webpack": "5.93.0", + "webpack": "5.94.0", "webpack-cli": "5.1.4" } }, "packages/virtue-virtue-back-press-dispatch": { "version": "0.0.1-SNAPSHOT", - "dependencies": { - "format-util": "^1.0.5" - }, "devDependencies": {} }, "packages/virtue-virtue-back-press-dispatch-test": { "version": "0.0.1-SNAPSHOT", - "dependencies": { - "format-util": "^1.0.5" - }, "devDependencies": { - "karma": "6.4.3", + "karma": "6.4.4", "karma-chrome-launcher": "3.2.0", "karma-mocha": "2.0.1", "karma-sourcemap-loader": "0.4.0", "karma-webpack": "5.0.1", - "mocha": "10.7.0", + "kotlin-web-helpers": "2.0.0", + "mocha": "10.7.3", "source-map-loader": "5.0.0", "typescript": "5.5.4", - "webpack": "5.93.0", + "webpack": "5.94.0", "webpack-cli": "5.1.4" } }, "packages/virtue-virtue-back-press-dispatch-wasm-js": { "version": "0.0.1-SNAPSHOT", - "dependencies": { - "format-util": "^1.0.5" - }, "devDependencies": {} }, "packages/virtue-virtue-back-press-dispatch-wasm-js-test": { "version": "0.0.1-SNAPSHOT", - "dependencies": { - "format-util": "^1.0.5" - }, "devDependencies": { - "karma": "6.4.3", + "karma": "6.4.4", "karma-chrome-launcher": "3.2.0", "karma-mocha": "2.0.1", "karma-sourcemap-loader": "0.4.0", "karma-webpack": "5.0.1", - "mocha": "10.7.0", + "kotlin-web-helpers": "2.0.0", + "mocha": "10.7.3", "source-map-loader": "5.0.0", "typescript": "5.5.4", - "webpack": "5.93.0", + "webpack": "5.94.0", "webpack-cli": "5.1.4" } }, "packages/virtue-virtue-browser-platform": { "version": "0.0.1-SNAPSHOT", - "dependencies": { - "format-util": "^1.0.5" - }, "devDependencies": {} }, "packages/virtue-virtue-browser-platform-test": { "version": "0.0.1-SNAPSHOT", - "dependencies": { - "format-util": "^1.0.5" - }, "devDependencies": { - "karma": "6.4.3", + "karma": "6.4.4", "karma-chrome-launcher": "3.2.0", "karma-mocha": "2.0.1", "karma-sourcemap-loader": "0.4.0", "karma-webpack": "5.0.1", - "mocha": "10.7.0", + "kotlin-web-helpers": "2.0.0", + "mocha": "10.7.3", "source-map-loader": "5.0.0", "typescript": "5.5.4", - "webpack": "5.93.0", + "webpack": "5.94.0", "webpack-cli": "5.1.4" } }, "packages/virtue-virtue-browser-platform-wasm-js": { "version": "0.0.1-SNAPSHOT", - "dependencies": { - "format-util": "^1.0.5" - }, "devDependencies": {} }, "packages/virtue-virtue-browser-platform-wasm-js-test": { "version": "0.0.1-SNAPSHOT", - "dependencies": { - "format-util": "^1.0.5" - }, "devDependencies": { - "karma": "6.4.3", + "karma": "6.4.4", "karma-chrome-launcher": "3.2.0", "karma-mocha": "2.0.1", "karma-sourcemap-loader": "0.4.0", "karma-webpack": "5.0.1", - "mocha": "10.7.0", + "kotlin-web-helpers": "2.0.0", + "mocha": "10.7.3", "source-map-loader": "5.0.0", "typescript": "5.5.4", - "webpack": "5.93.0", + "webpack": "5.94.0", "webpack-cli": "5.1.4" } }, "packages/virtue-virtue-config": { "version": "0.0.1-SNAPSHOT", - "dependencies": { - "format-util": "^1.0.5" - }, "devDependencies": {} }, "packages/virtue-virtue-config-test": { "version": "0.0.1-SNAPSHOT", - "dependencies": { - "format-util": "^1.0.5" - }, "devDependencies": { - "karma": "6.4.3", + "karma": "6.4.4", "karma-chrome-launcher": "3.2.0", "karma-mocha": "2.0.1", "karma-sourcemap-loader": "0.4.0", "karma-webpack": "5.0.1", - "mocha": "10.7.0", + "kotlin-web-helpers": "2.0.0", + "mocha": "10.7.3", "source-map-loader": "5.0.0", "typescript": "5.5.4", - "webpack": "5.93.0", + "webpack": "5.94.0", "webpack-cli": "5.1.4" } }, "packages/virtue-virtue-config-wasm-js": { "version": "0.0.1-SNAPSHOT", - "dependencies": { - "format-util": "^1.0.5" - }, "devDependencies": {} }, "packages/virtue-virtue-config-wasm-js-test": { "version": "0.0.1-SNAPSHOT", - "dependencies": { - "format-util": "^1.0.5" - }, "devDependencies": { - "karma": "6.4.3", + "karma": "6.4.4", "karma-chrome-launcher": "3.2.0", "karma-mocha": "2.0.1", "karma-sourcemap-loader": "0.4.0", "karma-webpack": "5.0.1", - "mocha": "10.7.0", + "kotlin-web-helpers": "2.0.0", + "mocha": "10.7.3", "source-map-loader": "5.0.0", "typescript": "5.5.4", - "webpack": "5.93.0", + "webpack": "5.94.0", "webpack-cli": "5.1.4" } }, "packages/virtue-virtue-crypto": { "version": "0.0.1-SNAPSHOT", - "dependencies": { - "format-util": "^1.0.5" - }, "devDependencies": {} }, "packages/virtue-virtue-crypto-test": { "version": "0.0.1-SNAPSHOT", - "dependencies": { - "format-util": "^1.0.5" - }, "devDependencies": { - "karma": "6.4.3", + "karma": "6.4.4", "karma-chrome-launcher": "3.2.0", "karma-mocha": "2.0.1", "karma-sourcemap-loader": "0.4.0", "karma-webpack": "5.0.1", - "mocha": "10.7.0", + "kotlin-web-helpers": "2.0.0", + "mocha": "10.7.3", "source-map-loader": "5.0.0", "typescript": "5.5.4", - "webpack": "5.93.0", + "webpack": "5.94.0", "webpack-cli": "5.1.4" } }, "packages/virtue-virtue-crypto-wasm-js": { "version": "0.0.1-SNAPSHOT", - "dependencies": { - "format-util": "^1.0.5" - }, "devDependencies": {} }, "packages/virtue-virtue-crypto-wasm-js-test": { "version": "0.0.1-SNAPSHOT", - "dependencies": { - "format-util": "^1.0.5" - }, "devDependencies": { - "karma": "6.4.3", + "karma": "6.4.4", "karma-chrome-launcher": "3.2.0", "karma-mocha": "2.0.1", "karma-sourcemap-loader": "0.4.0", "karma-webpack": "5.0.1", - "mocha": "10.7.0", + "kotlin-web-helpers": "2.0.0", + "mocha": "10.7.3", "source-map-loader": "5.0.0", "typescript": "5.5.4", - "webpack": "5.93.0", + "webpack": "5.94.0", "webpack-cli": "5.1.4" } }, "packages/virtue-virtue-di-components": { "version": "0.0.1-SNAPSHOT", - "dependencies": { - "format-util": "^1.0.5" - }, "devDependencies": {} }, "packages/virtue-virtue-di-components-test": { "version": "0.0.1-SNAPSHOT", - "dependencies": { - "format-util": "^1.0.5" - }, "devDependencies": { - "karma": "6.4.3", + "karma": "6.4.4", "karma-chrome-launcher": "3.2.0", "karma-mocha": "2.0.1", "karma-sourcemap-loader": "0.4.0", "karma-webpack": "5.0.1", - "mocha": "10.7.0", + "kotlin-web-helpers": "2.0.0", + "mocha": "10.7.3", "source-map-loader": "5.0.0", "typescript": "5.5.4", - "webpack": "5.93.0", + "webpack": "5.94.0", "webpack-cli": "5.1.4" } }, "packages/virtue-virtue-di-components-wasm-js": { "version": "0.0.1-SNAPSHOT", - "dependencies": { - "format-util": "^1.0.5" - }, "devDependencies": {} }, "packages/virtue-virtue-di-components-wasm-js-test": { "version": "0.0.1-SNAPSHOT", - "dependencies": { - "format-util": "^1.0.5" - }, "devDependencies": { - "karma": "6.4.3", + "karma": "6.4.4", "karma-chrome-launcher": "3.2.0", "karma-mocha": "2.0.1", "karma-sourcemap-loader": "0.4.0", "karma-webpack": "5.0.1", - "mocha": "10.7.0", + "kotlin-web-helpers": "2.0.0", + "mocha": "10.7.3", "source-map-loader": "5.0.0", "typescript": "5.5.4", - "webpack": "5.93.0", + "webpack": "5.94.0", "webpack-cli": "5.1.4" } }, "packages/virtue-virtue-di-scopes": { "version": "0.0.1-SNAPSHOT", - "dependencies": { - "format-util": "^1.0.5" - }, "devDependencies": {} }, "packages/virtue-virtue-di-scopes-test": { "version": "0.0.1-SNAPSHOT", - "dependencies": { - "format-util": "^1.0.5" - }, "devDependencies": { - "karma": "6.4.3", + "karma": "6.4.4", "karma-chrome-launcher": "3.2.0", "karma-mocha": "2.0.1", "karma-sourcemap-loader": "0.4.0", "karma-webpack": "5.0.1", - "mocha": "10.7.0", + "kotlin-web-helpers": "2.0.0", + "mocha": "10.7.3", "source-map-loader": "5.0.0", "typescript": "5.5.4", - "webpack": "5.93.0", + "webpack": "5.94.0", "webpack-cli": "5.1.4" } }, "packages/virtue-virtue-di-scopes-wasm-js": { "version": "0.0.1-SNAPSHOT", - "dependencies": { - "format-util": "^1.0.5" - }, "devDependencies": {} }, "packages/virtue-virtue-di-scopes-wasm-js-test": { "version": "0.0.1-SNAPSHOT", - "dependencies": { - "format-util": "^1.0.5" - }, "devDependencies": { - "karma": "6.4.3", + "karma": "6.4.4", "karma-chrome-launcher": "3.2.0", "karma-mocha": "2.0.1", "karma-sourcemap-loader": "0.4.0", "karma-webpack": "5.0.1", - "mocha": "10.7.0", + "kotlin-web-helpers": "2.0.0", + "mocha": "10.7.3", "source-map-loader": "5.0.0", "typescript": "5.5.4", - "webpack": "5.93.0", + "webpack": "5.94.0", "webpack-cli": "5.1.4" } }, "packages/virtue-virtue-init": { "version": "0.0.1-SNAPSHOT", - "dependencies": { - "format-util": "^1.0.5" - }, "devDependencies": {} }, "packages/virtue-virtue-init-test": { "version": "0.0.1-SNAPSHOT", - "dependencies": { - "format-util": "^1.0.5" - }, "devDependencies": { - "karma": "6.4.3", + "karma": "6.4.4", "karma-chrome-launcher": "3.2.0", "karma-mocha": "2.0.1", "karma-sourcemap-loader": "0.4.0", "karma-webpack": "5.0.1", - "mocha": "10.7.0", + "kotlin-web-helpers": "2.0.0", + "mocha": "10.7.3", "source-map-loader": "5.0.0", "typescript": "5.5.4", - "webpack": "5.93.0", + "webpack": "5.94.0", "webpack-cli": "5.1.4" } }, "packages/virtue-virtue-init-wasm-js": { "version": "0.0.1-SNAPSHOT", - "dependencies": { - "format-util": "^1.0.5" - }, "devDependencies": {} }, "packages/virtue-virtue-init-wasm-js-test": { "version": "0.0.1-SNAPSHOT", - "dependencies": { - "format-util": "^1.0.5" - }, "devDependencies": { - "karma": "6.4.3", + "karma": "6.4.4", "karma-chrome-launcher": "3.2.0", "karma-mocha": "2.0.1", "karma-sourcemap-loader": "0.4.0", "karma-webpack": "5.0.1", - "mocha": "10.7.0", + "kotlin-web-helpers": "2.0.0", + "mocha": "10.7.3", "source-map-loader": "5.0.0", "typescript": "5.5.4", - "webpack": "5.93.0", + "webpack": "5.94.0", "webpack-cli": "5.1.4" } }, "packages/virtue-virtue-paths": { "version": "0.0.1-SNAPSHOT", - "dependencies": { - "format-util": "^1.0.5" - }, "devDependencies": {} }, "packages/virtue-virtue-paths-test": { "version": "0.0.1-SNAPSHOT", - "dependencies": { - "format-util": "^1.0.5" - }, "devDependencies": { - "karma": "6.4.3", + "karma": "6.4.4", "karma-chrome-launcher": "3.2.0", "karma-mocha": "2.0.1", "karma-sourcemap-loader": "0.4.0", "karma-webpack": "5.0.1", - "mocha": "10.7.0", + "kotlin-web-helpers": "2.0.0", + "mocha": "10.7.3", "source-map-loader": "5.0.0", "typescript": "5.5.4", - "webpack": "5.93.0", + "webpack": "5.94.0", "webpack-cli": "5.1.4" } }, "packages/virtue-virtue-paths-wasm-js": { "version": "0.0.1-SNAPSHOT", - "dependencies": { - "format-util": "^1.0.5" - }, "devDependencies": {} }, "packages/virtue-virtue-paths-wasm-js-test": { "version": "0.0.1-SNAPSHOT", - "dependencies": { - "format-util": "^1.0.5" - }, "devDependencies": { - "karma": "6.4.3", + "karma": "6.4.4", "karma-chrome-launcher": "3.2.0", "karma-mocha": "2.0.1", "karma-sourcemap-loader": "0.4.0", "karma-webpack": "5.0.1", - "mocha": "10.7.0", + "kotlin-web-helpers": "2.0.0", + "mocha": "10.7.3", "source-map-loader": "5.0.0", "typescript": "5.5.4", - "webpack": "5.93.0", + "webpack": "5.94.0", "webpack-cli": "5.1.4" } }, "packages/virtue-virtue-platform": { "version": "0.0.1-SNAPSHOT", - "dependencies": { - "format-util": "^1.0.5" - }, "devDependencies": {} }, "packages/virtue-virtue-platform-test": { "version": "0.0.1-SNAPSHOT", - "dependencies": { - "format-util": "^1.0.5" - }, "devDependencies": { - "karma": "6.4.3", + "karma": "6.4.4", "karma-chrome-launcher": "3.2.0", "karma-mocha": "2.0.1", "karma-sourcemap-loader": "0.4.0", "karma-webpack": "5.0.1", - "mocha": "10.7.0", + "kotlin-web-helpers": "2.0.0", + "mocha": "10.7.3", "source-map-loader": "5.0.0", "typescript": "5.5.4", - "webpack": "5.93.0", + "webpack": "5.94.0", "webpack-cli": "5.1.4" } }, "packages/virtue-virtue-platform-wasm-js": { "version": "0.0.1-SNAPSHOT", - "dependencies": { - "format-util": "^1.0.5" - }, "devDependencies": {} }, "packages/virtue-virtue-platform-wasm-js-test": { "version": "0.0.1-SNAPSHOT", - "dependencies": { - "format-util": "^1.0.5" - }, "devDependencies": { - "karma": "6.4.3", + "karma": "6.4.4", "karma-chrome-launcher": "3.2.0", "karma-mocha": "2.0.1", "karma-sourcemap-loader": "0.4.0", "karma-webpack": "5.0.1", - "mocha": "10.7.0", + "kotlin-web-helpers": "2.0.0", + "mocha": "10.7.3", "source-map-loader": "5.0.0", "typescript": "5.5.4", - "webpack": "5.93.0", + "webpack": "5.94.0", "webpack-cli": "5.1.4" } }, @@ -6361,51 +6283,41 @@ }, "packages/virtue-virtue-session-state": { "version": "0.0.1-SNAPSHOT", - "dependencies": { - "format-util": "^1.0.5" - }, "devDependencies": {} }, "packages/virtue-virtue-session-state-test": { "version": "0.0.1-SNAPSHOT", - "dependencies": { - "format-util": "^1.0.5" - }, "devDependencies": { - "karma": "6.4.3", + "karma": "6.4.4", "karma-chrome-launcher": "3.2.0", "karma-mocha": "2.0.1", "karma-sourcemap-loader": "0.4.0", "karma-webpack": "5.0.1", - "mocha": "10.7.0", + "kotlin-web-helpers": "2.0.0", + "mocha": "10.7.3", "source-map-loader": "5.0.0", "typescript": "5.5.4", - "webpack": "5.93.0", + "webpack": "5.94.0", "webpack-cli": "5.1.4" } }, "packages/virtue-virtue-session-state-wasm-js": { "version": "0.0.1-SNAPSHOT", - "dependencies": { - "format-util": "^1.0.5" - }, "devDependencies": {} }, "packages/virtue-virtue-session-state-wasm-js-test": { "version": "0.0.1-SNAPSHOT", - "dependencies": { - "format-util": "^1.0.5" - }, "devDependencies": { - "karma": "6.4.3", + "karma": "6.4.4", "karma-chrome-launcher": "3.2.0", "karma-mocha": "2.0.1", "karma-sourcemap-loader": "0.4.0", "karma-webpack": "5.0.1", - "mocha": "10.7.0", + "kotlin-web-helpers": "2.0.0", + "mocha": "10.7.3", "source-map-loader": "5.0.0", "typescript": "5.5.4", - "webpack": "5.93.0", + "webpack": "5.94.0", "webpack-cli": "5.1.4" } }, @@ -6416,15 +6328,16 @@ "format-util": "^1.0.5" }, "devDependencies": { - "karma": "6.4.3", + "karma": "6.4.4", "karma-chrome-launcher": "3.2.0", "karma-mocha": "2.0.1", "karma-sourcemap-loader": "0.4.0", "karma-webpack": "5.0.1", - "mocha": "10.7.0", + "kotlin-web-helpers": "2.0.0", + "mocha": "10.7.3", "source-map-loader": "5.0.0", "typescript": "5.5.4", - "webpack": "5.93.0", + "webpack": "5.94.0", "webpack-cli": "5.1.4" } }, @@ -6443,73 +6356,61 @@ "format-util": "^1.0.5" }, "devDependencies": { - "karma": "6.4.3", + "karma": "6.4.4", "karma-chrome-launcher": "3.2.0", "karma-mocha": "2.0.1", "karma-sourcemap-loader": "0.4.0", "karma-webpack": "5.0.1", - "mocha": "10.7.0", + "kotlin-web-helpers": "2.0.0", + "mocha": "10.7.3", "source-map-loader": "5.0.0", "typescript": "5.5.4", - "webpack": "5.93.0", + "webpack": "5.94.0", "webpack-cli": "5.1.4" } }, "packages/virtue-virtue-storage-kv": { "version": "0.0.1-SNAPSHOT", - "dependencies": { - "format-util": "^1.0.5" - }, "devDependencies": {} }, "packages/virtue-virtue-storage-kv-test": { "version": "0.0.1-SNAPSHOT", - "dependencies": { - "format-util": "^1.0.5" - }, "devDependencies": { - "karma": "6.4.3", + "karma": "6.4.4", "karma-chrome-launcher": "3.2.0", "karma-mocha": "2.0.1", "karma-sourcemap-loader": "0.4.0", "karma-webpack": "5.0.1", - "mocha": "10.7.0", + "kotlin-web-helpers": "2.0.0", + "mocha": "10.7.3", "source-map-loader": "5.0.0", "typescript": "5.5.4", - "webpack": "5.93.0", + "webpack": "5.94.0", "webpack-cli": "5.1.4" } }, "packages/virtue-virtue-storage-kv-wasm-js": { "version": "0.0.1-SNAPSHOT", - "dependencies": { - "format-util": "^1.0.5" - }, "devDependencies": {} }, "packages/virtue-virtue-storage-kv-wasm-js-test": { "version": "0.0.1-SNAPSHOT", - "dependencies": { - "format-util": "^1.0.5" - }, "devDependencies": { - "karma": "6.4.3", + "karma": "6.4.4", "karma-chrome-launcher": "3.2.0", "karma-mocha": "2.0.1", "karma-sourcemap-loader": "0.4.0", "karma-webpack": "5.0.1", - "mocha": "10.7.0", + "kotlin-web-helpers": "2.0.0", + "mocha": "10.7.3", "source-map-loader": "5.0.0", "typescript": "5.5.4", - "webpack": "5.93.0", + "webpack": "5.94.0", "webpack-cli": "5.1.4" } }, "packages/virtue-virtue-theme": { "version": "0.0.1-SNAPSHOT", - "dependencies": { - "format-util": "^1.0.5" - }, "devDependencies": {} }, "packages/virtue-virtue-theme-compose": { @@ -6527,15 +6428,16 @@ "format-util": "^1.0.5" }, "devDependencies": { - "karma": "6.4.3", + "karma": "6.4.4", "karma-chrome-launcher": "3.2.0", "karma-mocha": "2.0.1", "karma-sourcemap-loader": "0.4.0", "karma-webpack": "5.0.1", - "mocha": "10.7.0", + "kotlin-web-helpers": "2.0.0", + "mocha": "10.7.3", "source-map-loader": "5.0.0", "typescript": "5.5.4", - "webpack": "5.93.0", + "webpack": "5.94.0", "webpack-cli": "5.1.4" } }, @@ -6554,108 +6456,92 @@ "format-util": "^1.0.5" }, "devDependencies": { - "karma": "6.4.3", + "karma": "6.4.4", "karma-chrome-launcher": "3.2.0", "karma-mocha": "2.0.1", "karma-sourcemap-loader": "0.4.0", "karma-webpack": "5.0.1", - "mocha": "10.7.0", + "kotlin-web-helpers": "2.0.0", + "mocha": "10.7.3", "source-map-loader": "5.0.0", "typescript": "5.5.4", - "webpack": "5.93.0", + "webpack": "5.94.0", "webpack-cli": "5.1.4" } }, "packages/virtue-virtue-theme-test": { "version": "0.0.1-SNAPSHOT", - "dependencies": { - "format-util": "^1.0.5" - }, "devDependencies": { - "karma": "6.4.3", + "karma": "6.4.4", "karma-chrome-launcher": "3.2.0", "karma-mocha": "2.0.1", "karma-sourcemap-loader": "0.4.0", "karma-webpack": "5.0.1", - "mocha": "10.7.0", + "kotlin-web-helpers": "2.0.0", + "mocha": "10.7.3", "source-map-loader": "5.0.0", "typescript": "5.5.4", - "webpack": "5.93.0", + "webpack": "5.94.0", "webpack-cli": "5.1.4" } }, "packages/virtue-virtue-theme-wasm-js": { "version": "0.0.1-SNAPSHOT", - "dependencies": { - "format-util": "^1.0.5" - }, "devDependencies": {} }, "packages/virtue-virtue-theme-wasm-js-test": { "version": "0.0.1-SNAPSHOT", - "dependencies": { - "format-util": "^1.0.5" - }, "devDependencies": { - "karma": "6.4.3", + "karma": "6.4.4", "karma-chrome-launcher": "3.2.0", "karma-mocha": "2.0.1", "karma-sourcemap-loader": "0.4.0", "karma-webpack": "5.0.1", - "mocha": "10.7.0", + "kotlin-web-helpers": "2.0.0", + "mocha": "10.7.3", "source-map-loader": "5.0.0", "typescript": "5.5.4", - "webpack": "5.93.0", + "webpack": "5.94.0", "webpack-cli": "5.1.4" } }, "packages/virtue-virtue-utils": { "version": "0.0.1-SNAPSHOT", - "dependencies": { - "format-util": "^1.0.5" - }, "devDependencies": {} }, "packages/virtue-virtue-utils-test": { "version": "0.0.1-SNAPSHOT", - "dependencies": { - "format-util": "^1.0.5" - }, "devDependencies": { - "karma": "6.4.3", + "karma": "6.4.4", "karma-chrome-launcher": "3.2.0", "karma-mocha": "2.0.1", "karma-sourcemap-loader": "0.4.0", "karma-webpack": "5.0.1", - "mocha": "10.7.0", + "kotlin-web-helpers": "2.0.0", + "mocha": "10.7.3", "source-map-loader": "5.0.0", "typescript": "5.5.4", - "webpack": "5.93.0", + "webpack": "5.94.0", "webpack-cli": "5.1.4" } }, "packages/virtue-virtue-utils-wasm-js": { "version": "0.0.1-SNAPSHOT", - "dependencies": { - "format-util": "^1.0.5" - }, "devDependencies": {} }, "packages/virtue-virtue-utils-wasm-js-test": { "version": "0.0.1-SNAPSHOT", - "dependencies": { - "format-util": "^1.0.5" - }, "devDependencies": { - "karma": "6.4.3", + "karma": "6.4.4", "karma-chrome-launcher": "3.2.0", "karma-mocha": "2.0.1", "karma-sourcemap-loader": "0.4.0", "karma-webpack": "5.0.1", - "mocha": "10.7.0", + "kotlin-web-helpers": "2.0.0", + "mocha": "10.7.3", "source-map-loader": "5.0.0", "typescript": "5.5.4", - "webpack": "5.93.0", + "webpack": "5.94.0", "webpack-cli": "5.1.4" } } diff --git a/samples/auth/shared/build.gradle.kts b/samples/auth/shared/build.gradle.kts index f67a742..67a25ce 100644 --- a/samples/auth/shared/build.gradle.kts +++ b/samples/auth/shared/build.gradle.kts @@ -65,3 +65,12 @@ dependencyAnalysis { ksp { arg("me.tatarka.inject.generateCompanionExtensions", "true") } + +// needed until https://github.com/google/ksp/issues/2243 is resolved +tasks.all { + if(name.startsWith("kspKotlinIos")) { + afterEvaluate { + setOnlyIf { true } + } + } +} diff --git a/samples/auth/shared/src/commonMain/kotlin/com/eygraber/virtue/samples/auth/shared/AuthDestination.kt b/samples/auth/shared/src/commonMain/kotlin/com/eygraber/virtue/samples/auth/shared/AuthDestination.kt index d55828d..96d73d6 100644 --- a/samples/auth/shared/src/commonMain/kotlin/com/eygraber/virtue/samples/auth/shared/AuthDestination.kt +++ b/samples/auth/shared/src/commonMain/kotlin/com/eygraber/virtue/samples/auth/shared/AuthDestination.kt @@ -6,16 +6,10 @@ import com.eygraber.virtue.di.scopes.DestinationSingleton import com.eygraber.virtue.session.VirtueDestination import com.eygraber.virtue.session.VirtueDestinationComponent import me.tatarka.inject.annotations.Provides -import kotlin.experimental.ExperimentalObjCRefinement -import kotlin.native.HiddenFromObjC -@OptIn(ExperimentalObjCRefinement::class) -@HiddenFromObjC abstract class AuthDestination : VirtueDestination() where I : Any, C : ViceCompositor, S : Any -@OptIn(ExperimentalObjCRefinement::class) -@HiddenFromObjC abstract class AuthDestinationWithEffects : VirtueDestination() where I : Any, C : ViceCompositor, E : ViceEffects, S : Any diff --git a/samples/auth/shared/src/commonMain/kotlin/com/eygraber/virtue/samples/auth/shared/logged_in/LoggedInDestination.kt b/samples/auth/shared/src/commonMain/kotlin/com/eygraber/virtue/samples/auth/shared/logged_in/LoggedInDestination.kt index 60fba38..78e4e7c 100644 --- a/samples/auth/shared/src/commonMain/kotlin/com/eygraber/virtue/samples/auth/shared/logged_in/LoggedInDestination.kt +++ b/samples/auth/shared/src/commonMain/kotlin/com/eygraber/virtue/samples/auth/shared/logged_in/LoggedInDestination.kt @@ -8,16 +8,12 @@ import com.eygraber.virtue.samples.auth.shared.Routes import me.tatarka.inject.annotations.Component import me.tatarka.inject.annotations.KmpComponentCreate import me.tatarka.inject.annotations.Provides -import kotlin.experimental.ExperimentalObjCRefinement -import kotlin.native.HiddenFromObjC @DestinationSingleton class LoggedInNavigator( val onNavigateToRoot: () -> Unit, ) -@OptIn(ExperimentalObjCRefinement::class) -@HiddenFromObjC class LoggedInDestination( override val parentComponent: AuthSessionComponent, private val navigator: LoggedInNavigator, diff --git a/samples/auth/shared/src/commonMain/kotlin/com/eygraber/virtue/samples/auth/shared/logged_in/LoggedInStringsSource.kt b/samples/auth/shared/src/commonMain/kotlin/com/eygraber/virtue/samples/auth/shared/logged_in/LoggedInStringsSource.kt index 3cb78b4..3a0a239 100644 --- a/samples/auth/shared/src/commonMain/kotlin/com/eygraber/virtue/samples/auth/shared/logged_in/LoggedInStringsSource.kt +++ b/samples/auth/shared/src/commonMain/kotlin/com/eygraber/virtue/samples/auth/shared/logged_in/LoggedInStringsSource.kt @@ -7,13 +7,9 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOn import me.tatarka.inject.annotations.Inject -import kotlin.experimental.ExperimentalObjCRefinement -import kotlin.native.HiddenFromObjC @DestinationSingleton @Inject -@OptIn(ExperimentalObjCRefinement::class) -@HiddenFromObjC class LoggedInStringsSource( dispatcher: CoroutineDispatcher = Dispatchers.Default, ) : LoadableFlowSource() { diff --git a/samples/auth/shared/src/commonMain/kotlin/com/eygraber/virtue/samples/auth/shared/login/LoginDestination.kt b/samples/auth/shared/src/commonMain/kotlin/com/eygraber/virtue/samples/auth/shared/login/LoginDestination.kt index 1ea089e..9ddf706 100644 --- a/samples/auth/shared/src/commonMain/kotlin/com/eygraber/virtue/samples/auth/shared/login/LoginDestination.kt +++ b/samples/auth/shared/src/commonMain/kotlin/com/eygraber/virtue/samples/auth/shared/login/LoginDestination.kt @@ -8,16 +8,12 @@ import com.eygraber.virtue.samples.auth.shared.Routes import me.tatarka.inject.annotations.Component import me.tatarka.inject.annotations.KmpComponentCreate import me.tatarka.inject.annotations.Provides -import kotlin.experimental.ExperimentalObjCRefinement -import kotlin.native.HiddenFromObjC @DestinationSingleton class LoginNavigator( val onNavigateToLoggedIn: () -> Unit, ) -@OptIn(ExperimentalObjCRefinement::class) -@HiddenFromObjC class LoginDestination( navigator: LoginNavigator, override val parentComponent: AuthSessionComponent, diff --git a/samples/auth/shared/src/commonMain/kotlin/com/eygraber/virtue/samples/auth/shared/login/LoginStringsSource.kt b/samples/auth/shared/src/commonMain/kotlin/com/eygraber/virtue/samples/auth/shared/login/LoginStringsSource.kt index 28346c3..4768313 100644 --- a/samples/auth/shared/src/commonMain/kotlin/com/eygraber/virtue/samples/auth/shared/login/LoginStringsSource.kt +++ b/samples/auth/shared/src/commonMain/kotlin/com/eygraber/virtue/samples/auth/shared/login/LoginStringsSource.kt @@ -7,13 +7,9 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOn import me.tatarka.inject.annotations.Inject -import kotlin.experimental.ExperimentalObjCRefinement -import kotlin.native.HiddenFromObjC @DestinationSingleton @Inject -@OptIn(ExperimentalObjCRefinement::class) -@HiddenFromObjC class LoginStringsSource( dispatcher: CoroutineDispatcher = Dispatchers.Default, ) : LoadableFlowSource() { diff --git a/samples/auth/shared/src/commonMain/kotlin/com/eygraber/virtue/samples/auth/shared/logout/LogoutDestination.kt b/samples/auth/shared/src/commonMain/kotlin/com/eygraber/virtue/samples/auth/shared/logout/LogoutDestination.kt index b16bd28..3e71fab 100644 --- a/samples/auth/shared/src/commonMain/kotlin/com/eygraber/virtue/samples/auth/shared/logout/LogoutDestination.kt +++ b/samples/auth/shared/src/commonMain/kotlin/com/eygraber/virtue/samples/auth/shared/logout/LogoutDestination.kt @@ -8,16 +8,12 @@ import com.eygraber.virtue.samples.auth.shared.Routes import me.tatarka.inject.annotations.Component import me.tatarka.inject.annotations.KmpComponentCreate import me.tatarka.inject.annotations.Provides -import kotlin.experimental.ExperimentalObjCRefinement -import kotlin.native.HiddenFromObjC @DestinationSingleton class LogoutNavigator( val onNavigateToRoot: () -> Unit, ) -@OptIn(ExperimentalObjCRefinement::class) -@HiddenFromObjC class LogoutDestination( navigator: LogoutNavigator, override val parentComponent: AuthSessionComponent, diff --git a/samples/auth/shared/src/commonMain/kotlin/com/eygraber/virtue/samples/auth/shared/logout/LogoutStringsSource.kt b/samples/auth/shared/src/commonMain/kotlin/com/eygraber/virtue/samples/auth/shared/logout/LogoutStringsSource.kt index a0b6035..f83f13e 100644 --- a/samples/auth/shared/src/commonMain/kotlin/com/eygraber/virtue/samples/auth/shared/logout/LogoutStringsSource.kt +++ b/samples/auth/shared/src/commonMain/kotlin/com/eygraber/virtue/samples/auth/shared/logout/LogoutStringsSource.kt @@ -7,13 +7,9 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOn import me.tatarka.inject.annotations.Inject -import kotlin.experimental.ExperimentalObjCRefinement -import kotlin.native.HiddenFromObjC @DestinationSingleton @Inject -@OptIn(ExperimentalObjCRefinement::class) -@HiddenFromObjC class LogoutStringsSource( dispatcher: CoroutineDispatcher = Dispatchers.Default, ) : LoadableFlowSource() { diff --git a/samples/auth/shared/src/commonMain/kotlin/com/eygraber/virtue/samples/auth/shared/root/RootDestination.kt b/samples/auth/shared/src/commonMain/kotlin/com/eygraber/virtue/samples/auth/shared/root/RootDestination.kt index f5ef2ef..44a3a81 100644 --- a/samples/auth/shared/src/commonMain/kotlin/com/eygraber/virtue/samples/auth/shared/root/RootDestination.kt +++ b/samples/auth/shared/src/commonMain/kotlin/com/eygraber/virtue/samples/auth/shared/root/RootDestination.kt @@ -8,8 +8,6 @@ import com.eygraber.virtue.samples.auth.shared.Routes import me.tatarka.inject.annotations.Component import me.tatarka.inject.annotations.KmpComponentCreate import me.tatarka.inject.annotations.Provides -import kotlin.experimental.ExperimentalObjCRefinement -import kotlin.native.HiddenFromObjC @DestinationSingleton class RootNavigator( @@ -18,8 +16,6 @@ class RootNavigator( val onNavigateToLoggedIn: (Routes) -> Unit, ) -@OptIn(ExperimentalObjCRefinement::class) -@HiddenFromObjC class RootDestination( navigator: RootNavigator, route: Routes.Root, diff --git a/samples/auth/shared/src/commonMain/kotlin/com/eygraber/virtue/samples/auth/shared/root/RootStringsSource.kt b/samples/auth/shared/src/commonMain/kotlin/com/eygraber/virtue/samples/auth/shared/root/RootStringsSource.kt index 713887b..36b470f 100644 --- a/samples/auth/shared/src/commonMain/kotlin/com/eygraber/virtue/samples/auth/shared/root/RootStringsSource.kt +++ b/samples/auth/shared/src/commonMain/kotlin/com/eygraber/virtue/samples/auth/shared/root/RootStringsSource.kt @@ -7,13 +7,9 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOn import me.tatarka.inject.annotations.Inject -import kotlin.experimental.ExperimentalObjCRefinement -import kotlin.native.HiddenFromObjC @DestinationSingleton @Inject -@OptIn(ExperimentalObjCRefinement::class) -@HiddenFromObjC class RootStringsSource( dispatcher: CoroutineDispatcher = Dispatchers.Default, ) : LoadableFlowSource() { diff --git a/samples/todo/shared/build.gradle.kts b/samples/todo/shared/build.gradle.kts index 77f812f..e9a72c2 100644 --- a/samples/todo/shared/build.gradle.kts +++ b/samples/todo/shared/build.gradle.kts @@ -56,3 +56,12 @@ dependencyAnalysis { ksp { arg("me.tatarka.inject.generateCompanionExtensions", "true") } + +// needed until https://github.com/google/ksp/issues/2243 is resolved +tasks.all { + if(name.startsWith("kspKotlinIos")) { + afterEvaluate { + setOnlyIf { true } + } + } +} diff --git a/samples/todo/shared/src/commonMain/kotlin/com/eygraber/virtue/samples/todo/shared/TodoDestination.kt b/samples/todo/shared/src/commonMain/kotlin/com/eygraber/virtue/samples/todo/shared/TodoDestination.kt index eb05f44..2773abf 100644 --- a/samples/todo/shared/src/commonMain/kotlin/com/eygraber/virtue/samples/todo/shared/TodoDestination.kt +++ b/samples/todo/shared/src/commonMain/kotlin/com/eygraber/virtue/samples/todo/shared/TodoDestination.kt @@ -6,11 +6,7 @@ import com.eygraber.virtue.di.scopes.DestinationSingleton import com.eygraber.virtue.session.VirtueDestination import com.eygraber.virtue.session.VirtueDestinationComponent import me.tatarka.inject.annotations.Provides -import kotlin.experimental.ExperimentalObjCRefinement -import kotlin.native.HiddenFromObjC -@OptIn(ExperimentalObjCRefinement::class) -@HiddenFromObjC abstract class TodoDestination : VirtueDestination() where I : Any, C : ViceCompositor, S : Any diff --git a/samples/todo/shared/src/commonMain/kotlin/com/eygraber/virtue/samples/todo/shared/about/AboutUsDestination.kt b/samples/todo/shared/src/commonMain/kotlin/com/eygraber/virtue/samples/todo/shared/about/AboutUsDestination.kt index 29b0a40..67d7fe7 100644 --- a/samples/todo/shared/src/commonMain/kotlin/com/eygraber/virtue/samples/todo/shared/about/AboutUsDestination.kt +++ b/samples/todo/shared/src/commonMain/kotlin/com/eygraber/virtue/samples/todo/shared/about/AboutUsDestination.kt @@ -8,16 +8,12 @@ import com.eygraber.virtue.samples.todo.shared.TodoSessionComponent import me.tatarka.inject.annotations.Component import me.tatarka.inject.annotations.KmpComponentCreate import me.tatarka.inject.annotations.Provides -import kotlin.experimental.ExperimentalObjCRefinement -import kotlin.native.HiddenFromObjC @DestinationSingleton class AboutUsNavigator( val onNavigateBack: () -> Unit, ) -@OptIn(ExperimentalObjCRefinement::class) -@HiddenFromObjC class AboutUsDestination( onNavigateBack: () -> Unit, override val parentComponent: TodoSessionComponent, diff --git a/samples/todo/shared/src/commonMain/kotlin/com/eygraber/virtue/samples/todo/shared/details/DetailsDestination.kt b/samples/todo/shared/src/commonMain/kotlin/com/eygraber/virtue/samples/todo/shared/details/DetailsDestination.kt index 8336bcb..1c375e1 100644 --- a/samples/todo/shared/src/commonMain/kotlin/com/eygraber/virtue/samples/todo/shared/details/DetailsDestination.kt +++ b/samples/todo/shared/src/commonMain/kotlin/com/eygraber/virtue/samples/todo/shared/details/DetailsDestination.kt @@ -8,16 +8,12 @@ import com.eygraber.virtue.samples.todo.shared.TodoSessionComponent import me.tatarka.inject.annotations.Component import me.tatarka.inject.annotations.KmpComponentCreate import me.tatarka.inject.annotations.Provides -import kotlin.experimental.ExperimentalObjCRefinement -import kotlin.native.HiddenFromObjC @DestinationSingleton class DetailsNavigator( val onNavigateBack: () -> Unit, ) -@OptIn(ExperimentalObjCRefinement::class) -@HiddenFromObjC class DetailsDestination( op: Routes.Details, onNavigateBack: () -> Unit, diff --git a/samples/todo/shared/src/commonMain/kotlin/com/eygraber/virtue/samples/todo/shared/home/HomeDestination.kt b/samples/todo/shared/src/commonMain/kotlin/com/eygraber/virtue/samples/todo/shared/home/HomeDestination.kt index aba4f1c..0a8f440 100644 --- a/samples/todo/shared/src/commonMain/kotlin/com/eygraber/virtue/samples/todo/shared/home/HomeDestination.kt +++ b/samples/todo/shared/src/commonMain/kotlin/com/eygraber/virtue/samples/todo/shared/home/HomeDestination.kt @@ -8,8 +8,6 @@ import com.eygraber.virtue.samples.todo.shared.TodoSessionComponent import me.tatarka.inject.annotations.Component import me.tatarka.inject.annotations.KmpComponentCreate import me.tatarka.inject.annotations.Provides -import kotlin.experimental.ExperimentalObjCRefinement -import kotlin.native.HiddenFromObjC @DestinationSingleton class HomeNavigator( @@ -18,8 +16,6 @@ class HomeNavigator( val onNavigateToSettings: () -> Unit, ) -@OptIn(ExperimentalObjCRefinement::class) -@HiddenFromObjC class HomeDestination( onNavigateToCreateItem: () -> Unit, onNavigateToUpdateItem: (String) -> Unit, diff --git a/samples/todo/shared/src/commonMain/kotlin/com/eygraber/virtue/samples/todo/shared/settings/SettingsDestination.kt b/samples/todo/shared/src/commonMain/kotlin/com/eygraber/virtue/samples/todo/shared/settings/SettingsDestination.kt index 0cf1ec6..f7a4837 100644 --- a/samples/todo/shared/src/commonMain/kotlin/com/eygraber/virtue/samples/todo/shared/settings/SettingsDestination.kt +++ b/samples/todo/shared/src/commonMain/kotlin/com/eygraber/virtue/samples/todo/shared/settings/SettingsDestination.kt @@ -8,8 +8,6 @@ import com.eygraber.virtue.samples.todo.shared.TodoSessionComponent import me.tatarka.inject.annotations.Component import me.tatarka.inject.annotations.KmpComponentCreate import me.tatarka.inject.annotations.Provides -import kotlin.experimental.ExperimentalObjCRefinement -import kotlin.native.HiddenFromObjC @DestinationSingleton class SettingsNavigator( @@ -17,8 +15,6 @@ class SettingsNavigator( val onNavigateToAboutUs: () -> Unit, ) -@OptIn(ExperimentalObjCRefinement::class) -@HiddenFromObjC class SettingsDestination( onNavigateBack: () -> Unit, onNavigateToAboutUs: () -> Unit, diff --git a/settings.gradle.kts b/settings.gradle.kts index 3b6a048..c5d7d2a 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -54,7 +54,7 @@ dependencyResolutionManagement { rootProject.name = "virtue" plugins { - id("com.eygraber.conventions.settings") version "0.0.79" + id("com.eygraber.conventions.settings") version "0.0.81" id("com.gradle.develocity") version "3.18.2" } diff --git a/virtue-app/src/androidMain/kotlin/com/eygraber/virtue/app/VirtueActivityDelegate.kt b/virtue-app/src/androidMain/kotlin/com/eygraber/virtue/app/VirtueActivityDelegate.kt index 9dcacd5..81a2604 100644 --- a/virtue-app/src/androidMain/kotlin/com/eygraber/virtue/app/VirtueActivityDelegate.kt +++ b/virtue-app/src/androidMain/kotlin/com/eygraber/virtue/app/VirtueActivityDelegate.kt @@ -67,7 +67,14 @@ public class VirtueActivityDelegate( callSuper() - val initialRoute = activity.intent?.data?.toUri()?.let(createDeepLink)?.route ?: sessionParams().initialRoute + val initialRoute = + activity + .intent + ?.data + ?.toUri() + ?.let(createDeepLink) + ?.route + ?: sessionParams().initialRoute activity.setContent { if(isEdgeToEdge) { diff --git a/virtue-auth/src/commonMain/kotlin/com/eygraber/virtue/auth/VirtueAuth.kt b/virtue-auth/src/commonMain/kotlin/com/eygraber/virtue/auth/VirtueAuth.kt index 13017cb..ad9815f 100644 --- a/virtue-auth/src/commonMain/kotlin/com/eygraber/virtue/auth/VirtueAuth.kt +++ b/virtue-auth/src/commonMain/kotlin/com/eygraber/virtue/auth/VirtueAuth.kt @@ -91,21 +91,19 @@ public class VirtueAuth( public val stateFlow: Flow = state.filterNotNull() public suspend fun initialize() { - val isLoggingOut = deviceStorage.getInt(LOG_OUT, IS_NOT_LOGGING_OUT) + val loggingOutFlag = deviceStorage.getInt(LOG_OUT, IS_NOT_LOGGING_OUT) state.value = when { - isLoggingOut != IS_NOT_LOGGING_OUT -> { - when(isLoggingOut) { - IS_LOGGING_OUT_AUTOMATICALLY -> State.LoggingOut.Automatically - IS_LOGGING_OUT_MANUALLY -> State.LoggingOut.Manually - IS_LOGGED_OUT -> State.LoggedOut - else -> State.Error - } + loggingOutFlag != IS_NOT_LOGGING_OUT -> when(loggingOutFlag) { + IS_LOGGING_OUT_AUTOMATICALLY -> State.LoggingOut.Automatically + IS_LOGGING_OUT_MANUALLY -> State.LoggingOut.Manually + IS_LOGGED_OUT -> State.LoggedOut + else -> State.Error } else -> when(val token = loadToken()) { null -> State.LoggedOut - else -> { + else -> if(token.isExpired() && token.expirationPolicy is ExpirationPolicy.Expire) { startLogout(State.LoggingOut.Automatically) State.LoggingOut.Automatically @@ -113,7 +111,6 @@ public class VirtueAuth( else { State.LoggedIn } - } } } } @@ -168,7 +165,7 @@ public class VirtueAuth( /** * Will return `true` if there is no token */ - public suspend fun isTokenExpired(): Boolean = loadToken()?.isExpired() ?: true + public suspend fun isTokenExpired(): Boolean = loadToken()?.isExpired() != false public suspend fun currentState(): State = stateFlow.first() diff --git a/virtue-browser-platform/build.gradle.kts b/virtue-browser-platform/build.gradle.kts index 84a0ec0..08f2287 100644 --- a/virtue-browser-platform/build.gradle.kts +++ b/virtue-browser-platform/build.gradle.kts @@ -14,16 +14,18 @@ kotlin { ) sourceSets { - webMain { - dependencies { - implementation(projects.virtueUtils) + wasmJsMain.dependencies { + implementation(libs.kotlinx.wasm.browser) + } + + webMain.dependencies { + implementation(projects.virtueUtils) - api(libs.indexedDb.core) - api(libs.indexedDb.externals) - implementation(libs.kotlinx.coroutines.core) + api(libs.indexedDb.core) + api(libs.indexedDb.externals) + implementation(libs.kotlinx.coroutines.core) - api(libs.kotlinInject.runtime) - } + api(libs.kotlinInject.runtime) } } } diff --git a/virtue-crypto/src/androidMain/kotlin/com/eygraber/virtue/crypto/VirtueCryptoKeyStore.android.kt b/virtue-crypto/src/androidMain/kotlin/com/eygraber/virtue/crypto/VirtueCryptoKeyStore.android.kt index 709a603..c2a8639 100644 --- a/virtue-crypto/src/androidMain/kotlin/com/eygraber/virtue/crypto/VirtueCryptoKeyStore.android.kt +++ b/virtue-crypto/src/androidMain/kotlin/com/eygraber/virtue/crypto/VirtueCryptoKeyStore.android.kt @@ -11,6 +11,7 @@ import dev.whyoleg.cryptography.algorithms.AES.Key import dev.whyoleg.cryptography.algorithms.SHA256 import dev.whyoleg.cryptography.operations.AuthenticatedCipher import dev.whyoleg.cryptography.providers.jdk.JDK +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Deferred import kotlinx.coroutines.Dispatchers @@ -52,8 +53,9 @@ public actual class VirtueCryptoKeyStore( alias: String, shouldFailInInsecureEnvironments: Boolean, isDeviceUnlockRequired: Boolean, + dispatcher: CoroutineDispatcher, ): KeyStoreResult = - withContext(Dispatchers.IO) { + withContext(dispatcher) { runCatchingCoroutine { val keyStorePasswordAsync = loadKeyStorePassword(isDeviceUnlockRequired) val keyStore = loadOrCreateKeyStore( @@ -63,17 +65,21 @@ public actual class VirtueCryptoKeyStore( when { keyStore == null -> KeyStoreResult.Error.InsecureEnvironment - keyStore.isKeyEntry(alias) -> { + keyStore.isKeyEntry(alias) -> if(keyStore.entryInstanceOf(alias, SecretKeyEntry::class.java)) { - val key = keyStore.getKey(alias, alias.secretKeyPassword()) as SecretKey - KeyStoreResult.Success( - crypto.get(AES.GCM).keyDecoder().decodeFromByteArray(AES.Key.Format.RAW, key.encoded).cipher(), - ) + val key = keyStore.getKey(alias, alias.secretKeyPassword()) as? SecretKey + if(key == null) { + KeyStoreResult.Error.WrongKeyType + } + else { + KeyStoreResult.Success( + crypto.get(AES.GCM).keyDecoder().decodeFromByteArray(AES.Key.Format.RAW, key.encoded).cipher(), + ) + } } else { KeyStoreResult.Error.WrongKeyType } - } else -> KeyStoreResult.Success( crypto @@ -98,7 +104,8 @@ public actual class VirtueCryptoKeyStore( private suspend fun loadOrCreateKeyStore( keyStorePasswordAsync: Deferred, - ): KeyStore? = withContext(Dispatchers.IO) { + dispatcher: CoroutineDispatcher = Dispatchers.IO, + ): KeyStore? = withContext(dispatcher) { val keyStore = KeyStore.getInstance("BKS") val keyStorePassword = keyStorePasswordAsync.await() @@ -128,14 +135,14 @@ public actual class VirtueCryptoKeyStore( val secretKeyEntry = SecretKeyEntry(SecretKeySpec(rawBytes, "AES")) keyStore.setEntry(alias, secretKeyEntry, PasswordProtection(alias.secretKeyPassword())) - val saved = saveKeyStore( + val isSaved = saveKeyStore( keyStore = keyStore, keyStorePasswordAsync = keyStorePasswordAsync, ) Arrays.fill(rawBytes, 0) - check(saved) { + check(isSaved) { "Saving the KeyStore failed because of an insecure environment" } } @@ -158,7 +165,7 @@ public actual class VirtueCryptoKeyStore( val alias = "com.eygraber.virtue.crypto.AndroidCryptoKeyStore" val secretKey = if(keyStore.containsAlias(alias)) { - keyStore.getKey(alias, null) as SecretKey + keyStore.getKey(alias, null) as? SecretKey } else { val keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore") @@ -168,11 +175,11 @@ public actual class VirtueCryptoKeyStore( ).setBlockModes(KeyProperties.BLOCK_MODE_GCM) .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) .setKeySize(256) - .also { + .also { builder -> // there were a lot of issues related to using // setUnlockedDeviceRequired(true) before Android 15 (see docs) if(Build.VERSION.SDK_INT >= 35) { - it.setUnlockedDeviceRequired(isDeviceUnlockRequired) + builder.setUnlockedDeviceRequired(isDeviceUnlockRequired) } } .build() diff --git a/virtue-crypto/src/commonMain/kotlin/com/eygraber/virtue/crypto/VirtueCryptoKeyStore.kt b/virtue-crypto/src/commonMain/kotlin/com/eygraber/virtue/crypto/VirtueCryptoKeyStore.kt index cb590bd..40d7653 100644 --- a/virtue-crypto/src/commonMain/kotlin/com/eygraber/virtue/crypto/VirtueCryptoKeyStore.kt +++ b/virtue-crypto/src/commonMain/kotlin/com/eygraber/virtue/crypto/VirtueCryptoKeyStore.kt @@ -2,6 +2,8 @@ package com.eygraber.virtue.crypto import dev.whyoleg.cryptography.operations.AuthenticatedCipher import dev.whyoleg.cryptography.random.CryptographyRandom +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers import me.tatarka.inject.annotations.Inject public sealed interface KeyStoreResult { @@ -22,6 +24,7 @@ public expect class VirtueCryptoKeyStore { alias: String, shouldFailInInsecureEnvironments: Boolean = false, isDeviceUnlockRequired: Boolean = false, + dispatcher: CoroutineDispatcher = Dispatchers.Default, ): KeyStoreResult } diff --git a/virtue-crypto/src/iosMain/kotlin/com/eygraber/virtue/crypto/VirtueCryptoKeyStore.ios.kt b/virtue-crypto/src/iosMain/kotlin/com/eygraber/virtue/crypto/VirtueCryptoKeyStore.ios.kt index dc38384..97aa5f2 100644 --- a/virtue-crypto/src/iosMain/kotlin/com/eygraber/virtue/crypto/VirtueCryptoKeyStore.ios.kt +++ b/virtue-crypto/src/iosMain/kotlin/com/eygraber/virtue/crypto/VirtueCryptoKeyStore.ios.kt @@ -20,8 +20,7 @@ import kotlinx.cinterop.ptr import kotlinx.cinterop.reinterpret import kotlinx.cinterop.usePinned import kotlinx.cinterop.value -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.IO +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.withContext import me.tatarka.inject.annotations.Inject import platform.CoreFoundation.CFDictionaryCreate @@ -76,30 +75,32 @@ public actual class VirtueCryptoKeyStore { alias: String, shouldFailInInsecureEnvironments: Boolean, isDeviceUnlockRequired: Boolean, - ): KeyStoreResult = withContext(Dispatchers.IO) { - runCatchingCoroutine { - KeyStoreResult.Success( - when(val storedKey = retrieveKey(alias)) { - null -> - crypto - .get(AES.GCM) - .keyGenerator(Key.Size.B256) - .generateKey() - .also { key -> - storeKey(alias, key.encodeToByteArray(AES.Key.Format.RAW), isDeviceUnlockRequired) - } - .cipher() - - else -> - crypto - .get(AES.GCM) - .keyDecoder() - .decodeFromByteArray(AES.Key.Format.RAW, storedKey) - .cipher() - }, - ) - }.getOrElse(KeyStoreResult.Error::Generic) - } + dispatcher: CoroutineDispatcher, + ): KeyStoreResult = + withContext(dispatcher) { + runCatchingCoroutine { + KeyStoreResult.Success( + when(val storedKey = retrieveKey(alias)) { + null -> + crypto + .get(AES.GCM) + .keyGenerator(Key.Size.B256) + .generateKey() + .also { key -> + storeKey(alias, key.encodeToByteArray(AES.Key.Format.RAW), isDeviceUnlockRequired) + } + .cipher() + + else -> + crypto + .get(AES.GCM) + .keyDecoder() + .decodeFromByteArray(AES.Key.Format.RAW, storedKey) + .cipher() + }, + ) + }.getOrElse(KeyStoreResult.Error::Generic) + } private fun retrieveKey(alias: String): ByteArray? = cfRetain(alias) { cfAlias -> val cfValue = alloc() diff --git a/virtue-crypto/src/jvmMain/kotlin/com/eygraber/virtue/crypto/VirtueCryptoKeyStore.jvm.kt b/virtue-crypto/src/jvmMain/kotlin/com/eygraber/virtue/crypto/VirtueCryptoKeyStore.jvm.kt index 39bd116..6ff8c08 100644 --- a/virtue-crypto/src/jvmMain/kotlin/com/eygraber/virtue/crypto/VirtueCryptoKeyStore.jvm.kt +++ b/virtue-crypto/src/jvmMain/kotlin/com/eygraber/virtue/crypto/VirtueCryptoKeyStore.jvm.kt @@ -12,6 +12,7 @@ import dev.whyoleg.cryptography.algorithms.AES.Key import dev.whyoleg.cryptography.algorithms.SHA256 import dev.whyoleg.cryptography.operations.AuthenticatedCipher import dev.whyoleg.cryptography.providers.jdk.JDK +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Deferred import kotlinx.coroutines.Dispatchers @@ -48,8 +49,9 @@ public actual class VirtueCryptoKeyStore( alias: String, shouldFailInInsecureEnvironments: Boolean, isDeviceUnlockRequired: Boolean, + dispatcher: CoroutineDispatcher, ): KeyStoreResult = - withContext(Dispatchers.IO) { + withContext(dispatcher) { runCatchingCoroutine { val keyStorePasswordAsync = loadKeyStorePassword() val keyStore = loadOrCreateKeyStore( @@ -64,17 +66,21 @@ public actual class VirtueCryptoKeyStore( when { keyStore == null -> KeyStoreResult.Error.InsecureEnvironment - keyStore.isKeyEntry(alias) -> { + keyStore.isKeyEntry(alias) -> if(keyStore.entryInstanceOf(alias, SecretKeyEntry::class.java)) { - val key = keyStore.getKey(alias, secretKeyPassword) as SecretKey - KeyStoreResult.Success( - crypto.get(AES.GCM).keyDecoder().decodeFromByteArray(AES.Key.Format.RAW, key.encoded).cipher(), - ) + val key = keyStore.getKey(alias, secretKeyPassword) as? SecretKey + if(key == null) { + KeyStoreResult.Error.WrongKeyType + } + else { + KeyStoreResult.Success( + crypto.get(AES.GCM).keyDecoder().decodeFromByteArray(AES.Key.Format.RAW, key.encoded).cipher(), + ) + } } else { KeyStoreResult.Error.WrongKeyType } - } else -> KeyStoreResult.Success( crypto @@ -86,7 +92,7 @@ public actual class VirtueCryptoKeyStore( val secretKeyEntry = SecretKeyEntry(SecretKeySpec(rawBytes, "AES")) keyStore.setEntry(alias, secretKeyEntry, PasswordProtection(secretKeyPassword)) - val saved = saveKeyStore( + val isSaved = saveKeyStore( keyStore = keyStore, keyStorePasswordAsync = keyStorePasswordAsync, shouldFailInInsecureEnvironments = shouldFailInInsecureEnvironments, @@ -94,14 +100,16 @@ public actual class VirtueCryptoKeyStore( Arrays.fill(rawBytes, 0) - check(saved) { + check(isSaved) { "Saving the KeyStore failed because of an insecure environment" } } .cipher(), ) }.also { - keyStorePasswordAsync.await()?.let { Arrays.fill(it, Char.MIN_VALUE) } + keyStorePasswordAsync.await()?.let { passwordChars -> + Arrays.fill(passwordChars, Char.MIN_VALUE) + } Arrays.fill(secretKeyPassword, Char.MIN_VALUE) } }.getOrElse(KeyStoreResult.Error::Generic) @@ -110,7 +118,8 @@ public actual class VirtueCryptoKeyStore( private suspend fun loadOrCreateKeyStore( keyStorePasswordAsync: Deferred, shouldFailInInsecureEnvironments: Boolean, - ): KeyStore? = withContext(Dispatchers.IO) { + dispatcher: CoroutineDispatcher = Dispatchers.IO, + ): KeyStore? = withContext(dispatcher) { val keyStore = KeyStore.getInstance("PKCS12") val keyStorePassword = keyStorePasswordAsync.await() diff --git a/virtue-crypto/src/webMain/kotlin/com/eygraber/virtue/crypto/VirtueCryptoKeyStore.web.kt b/virtue-crypto/src/webMain/kotlin/com/eygraber/virtue/crypto/VirtueCryptoKeyStore.web.kt index a4f08eb..7280732 100644 --- a/virtue-crypto/src/webMain/kotlin/com/eygraber/virtue/crypto/VirtueCryptoKeyStore.web.kt +++ b/virtue-crypto/src/webMain/kotlin/com/eygraber/virtue/crypto/VirtueCryptoKeyStore.web.kt @@ -9,7 +9,7 @@ import dev.whyoleg.cryptography.algorithms.AES.Key import dev.whyoleg.cryptography.algorithms.SHA256 import dev.whyoleg.cryptography.operations.AuthenticatedCipher import dev.whyoleg.cryptography.providers.webcrypto.WebCrypto -import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.withContext import me.tatarka.inject.annotations.Inject import kotlin.io.encoding.Base64 @@ -34,36 +34,38 @@ public actual class VirtueCryptoKeyStore( alias: String, shouldFailInInsecureEnvironments: Boolean, isDeviceUnlockRequired: Boolean, - ): KeyStoreResult = withContext(Dispatchers.Default) { - runCatchingCoroutine { - when( - val storedKey = database.readFromKeyValueStore(key(alias)) - ) { - null -> KeyStoreResult.Success( - crypto - .get(AES.GCM) - .keyGenerator(Key.Size.B256) - .generateKey() - .also { key -> - val keyBase64 = Base64.Default.encode(key.encodeToByteArray(AES.Key.Format.RAW)) + dispatcher: CoroutineDispatcher, + ): KeyStoreResult = + withContext(dispatcher) { + runCatchingCoroutine { + when( + val storedKey = database.readFromKeyValueStore(key(alias)) + ) { + null -> KeyStoreResult.Success( + crypto + .get(AES.GCM) + .keyGenerator(Key.Size.B256) + .generateKey() + .also { key -> + val keyBase64 = Base64.Default.encode(key.encodeToByteArray(AES.Key.Format.RAW)) - database.writableKeyValueStore { store -> - store.put(item = keyBase64, key = key(alias)) + database.writableKeyValueStore { store -> + store.put(item = keyBase64, key = key(alias)) + } } - } - .cipher(), - ) + .cipher(), + ) - else -> KeyStoreResult.Success( - crypto - .get(AES.GCM) - .keyDecoder() - .decodeFromByteArray(AES.Key.Format.RAW, Base64.decode(storedKey.toString())) - .cipher(), - ) - } - }.getOrElse(KeyStoreResult.Error::Generic) - } + else -> KeyStoreResult.Success( + crypto + .get(AES.GCM) + .keyDecoder() + .decodeFromByteArray(AES.Key.Format.RAW, Base64.decode(storedKey.toString())) + .cipher(), + ) + } + }.getOrElse(KeyStoreResult.Error::Generic) + } private fun key(alias: String): IDBKey = IDBKey("com.eygraber.virtue.crypto.WebVirtueCryptoKeyStore.$alias") } diff --git a/virtue-di-components/build.gradle.kts b/virtue-di-components/build.gradle.kts index 48f4249..307e139 100644 --- a/virtue-di-components/build.gradle.kts +++ b/virtue-di-components/build.gradle.kts @@ -21,23 +21,23 @@ kotlin { } sourceSets { - androidMain { - dependencies { - api(projects.virtueAndroid) - } + androidMain.dependencies { + api(projects.virtueAndroid) } - commonMain { - dependencies { - api(projects.virtueConfig) - api(projects.virtueDiScopes) - api(projects.virtueInit) - api(projects.virtuePaths) - api(projects.virtueStorageKv) - api(projects.virtueTheme) - - api(libs.kotlinInject.runtime) - } + commonMain.dependencies { + api(projects.virtueConfig) + api(projects.virtueDiScopes) + api(projects.virtueInit) + api(projects.virtuePaths) + api(projects.virtueStorageKv) + api(projects.virtueTheme) + + api(libs.kotlinInject.runtime) + } + + wasmJsMain.dependencies { + implementation(libs.kotlinx.wasm.browser) } } } @@ -45,3 +45,12 @@ kotlin { ksp { arg("me.tatarka.inject.generateCompanionExtensions", "true") } + +// needed until https://github.com/google/ksp/issues/2243 is resolved +tasks.all { + if(name.startsWith("kspKotlinIos")) { + afterEvaluate { + setOnlyIf { true } + } + } +} diff --git a/virtue-init/src/commonMain/kotlin/com/eygraber/virtue/init/VersionMigrationManager.kt b/virtue-init/src/commonMain/kotlin/com/eygraber/virtue/init/VersionMigrationManager.kt index fb89ac8..b0207a9 100644 --- a/virtue-init/src/commonMain/kotlin/com/eygraber/virtue/init/VersionMigrationManager.kt +++ b/virtue-init/src/commonMain/kotlin/com/eygraber/virtue/init/VersionMigrationManager.kt @@ -4,6 +4,8 @@ import com.eygraber.virtue.crypto.KeyStoreResult import com.eygraber.virtue.di.scopes.AppSingleton import com.eygraber.virtue.storage.kv.DeviceKeyValueStorage import com.eygraber.virtue.storage.kv.edit +import kotlinx.coroutines.CancellationException +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -47,8 +49,9 @@ public class VersionMigrationManager( public suspend fun migrateIfNeeded( migrations: List, + dispatcher: CoroutineDispatcher = Dispatchers.Default, ): State { - withContext(Dispatchers.Default) { + withContext(dispatcher) { mutex.withLock { if(migrations.isValid()) { state.value = State.Running @@ -57,7 +60,7 @@ public class VersionMigrationManager( val persistedVersion = deviceStorage.getInt(MOST_RECENT_MIGRATED_VERSION, -1) val lastMigration = migrations.last() - runCatching { + try { migrations .filter { it.version in persistedVersion + 1..lastMigration.version } .forEach { migration -> @@ -76,7 +79,10 @@ public class VersionMigrationManager( throw t } } - }.getOrElse { error -> + } + catch(error: Throwable) { + if(error is CancellationException) throw error + if(state.value !is KeyStoreResult.Error) { state.value = State.Failed.Error(error = error) } diff --git a/virtue-init/src/commonMain/kotlin/com/eygraber/virtue/init/VirtueAppInitializer.kt b/virtue-init/src/commonMain/kotlin/com/eygraber/virtue/init/VirtueAppInitializer.kt index 6b99464..3013f67 100644 --- a/virtue-init/src/commonMain/kotlin/com/eygraber/virtue/init/VirtueAppInitializer.kt +++ b/virtue-init/src/commonMain/kotlin/com/eygraber/virtue/init/VirtueAppInitializer.kt @@ -20,13 +20,13 @@ public abstract class VirtueAppInitializer { public suspend fun isFirstOpen(): Boolean = firstOpen.filterNotNull().first() - private var initialized = false + private var isInitialized = false @OptIn(DelicateCoroutinesApi::class) public fun initialize() { - if(initialized) return + if(isInitialized) return - initialized = true + isInitialized = true if(firstOpen.value == null) { GlobalScope.launch { val isFirstOpen = deviceKeyValueStorage.getBoolean(FIRST_OPEN, true) diff --git a/virtue-paths/src/androidMain/kotlin/com/eygraber/virtue/paths/AndroidVirtuePaths.kt b/virtue-paths/src/androidMain/kotlin/com/eygraber/virtue/paths/AndroidVirtuePaths.kt index 26d23f0..92e4690 100644 --- a/virtue-paths/src/androidMain/kotlin/com/eygraber/virtue/paths/AndroidVirtuePaths.kt +++ b/virtue-paths/src/androidMain/kotlin/com/eygraber/virtue/paths/AndroidVirtuePaths.kt @@ -4,6 +4,7 @@ import android.content.Context import android.os.Environment import com.eygraber.virtue.android.AppContext import com.eygraber.virtue.di.scopes.AppSingleton +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import me.tatarka.inject.annotations.Inject @@ -57,8 +58,10 @@ public class AndroidVirtuePaths( } } -public suspend fun VirtuePaths.clearAllProjectData() { - withContext(Dispatchers.IO) { +public suspend fun VirtuePaths.clearAllProjectData( + dispatcher: CoroutineDispatcher = Dispatchers.IO, +) { + withContext(dispatcher) { File(projectCacheDir).deleteRecursively() File(projectConfigDir).deleteRecursively() File(projectDataDir).deleteRecursively() diff --git a/virtue-paths/src/jvmMain/kotlin/com/eygraber/virtue/paths/JvmVirtuePaths.kt b/virtue-paths/src/jvmMain/kotlin/com/eygraber/virtue/paths/JvmVirtuePaths.kt index 790c679..334535e 100644 --- a/virtue-paths/src/jvmMain/kotlin/com/eygraber/virtue/paths/JvmVirtuePaths.kt +++ b/virtue-paths/src/jvmMain/kotlin/com/eygraber/virtue/paths/JvmVirtuePaths.kt @@ -2,6 +2,7 @@ package com.eygraber.virtue.paths import com.eygraber.virtue.config.VirtueAppInfo import com.eygraber.virtue.di.scopes.AppSingleton +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import me.tatarka.inject.annotations.Inject @@ -98,8 +99,10 @@ public class JvmVirtuePaths( } } -public suspend fun VirtuePaths.clearAllProjectData() { - withContext(Dispatchers.IO) { +public suspend fun VirtuePaths.clearAllProjectData( + dispatcher: CoroutineDispatcher = Dispatchers.IO, +) { + withContext(dispatcher) { File(projectCacheDir).deleteRecursively() File(projectConfigDir).deleteRecursively() File(projectDataDir).deleteRecursively() diff --git a/virtue-session-state/build.gradle.kts b/virtue-session-state/build.gradle.kts index 703b806..31e7b86 100644 --- a/virtue-session-state/build.gradle.kts +++ b/virtue-session-state/build.gradle.kts @@ -16,12 +16,14 @@ kotlin { ) sourceSets { - commonMain { - dependencies { - api(projects.virtueDiScopes) + commonMain.dependencies { + api(projects.virtueDiScopes) - api(libs.kotlinInject.runtime) - } + api(libs.kotlinInject.runtime) + } + + wasmJsMain.dependencies { + implementation(libs.kotlinx.wasm.browser) } } } diff --git a/virtue-session/src/commonMain/kotlin/com/eygraber/virtue/session/VirtueDestination.kt b/virtue-session/src/commonMain/kotlin/com/eygraber/virtue/session/VirtueDestination.kt index 66624e5..10cf63c 100644 --- a/virtue-session/src/commonMain/kotlin/com/eygraber/virtue/session/VirtueDestination.kt +++ b/virtue-session/src/commonMain/kotlin/com/eygraber/virtue/session/VirtueDestination.kt @@ -4,11 +4,7 @@ import com.eygraber.vice.ViceCompositor import com.eygraber.vice.ViceEffects import com.eygraber.vice.nav.ViceDestination import me.tatarka.inject.annotations.Provides -import kotlin.experimental.ExperimentalObjCRefinement -import kotlin.native.HiddenFromObjC -@OptIn(ExperimentalObjCRefinement::class) -@HiddenFromObjC public abstract class VirtueDestination : ViceDestination() where I : Any, C : ViceCompositor, diff --git a/virtue-session/src/commonMain/kotlin/com/eygraber/virtue/session/history/History.kt b/virtue-session/src/commonMain/kotlin/com/eygraber/virtue/session/history/History.kt index a18e6ff..9a13c4b 100644 --- a/virtue-session/src/commonMain/kotlin/com/eygraber/virtue/session/history/History.kt +++ b/virtue-session/src/commonMain/kotlin/com/eygraber/virtue/session/history/History.kt @@ -23,10 +23,7 @@ internal interface History { val canMoveBack: Boolean val canMoveForward: Boolean - val isIgnoringPlatformChanges: Boolean - - // https://youtrack.jetbrains.com/issue/KT-70901 - should be fixed in 2.0.21 - fun isIgnoringPlatformChanges(value: Boolean) + var isIgnoringPlatformChanges: Boolean operator fun get(index: Int): Entry diff --git a/virtue-session/src/commonMain/kotlin/com/eygraber/virtue/session/history/TimelineHistory.kt b/virtue-session/src/commonMain/kotlin/com/eygraber/virtue/session/history/TimelineHistory.kt index a575753..2857d42 100644 --- a/virtue-session/src/commonMain/kotlin/com/eygraber/virtue/session/history/TimelineHistory.kt +++ b/virtue-session/src/commonMain/kotlin/com/eygraber/virtue/session/history/TimelineHistory.kt @@ -42,9 +42,6 @@ internal class TimelineHistory private constructor( override val canMoveForward: Boolean get() = timeline.value.current < timeline.value.lastIndex override var isIgnoringPlatformChanges: Boolean = false - override fun isIgnoringPlatformChanges(value: Boolean) { - isIgnoringPlatformChanges = value - } override fun get(index: Int): History.Entry = timeline.value.entries[index] diff --git a/virtue-session/src/commonMain/kotlin/com/eygraber/virtue/session/nav/VirtueNavController.kt b/virtue-session/src/commonMain/kotlin/com/eygraber/virtue/session/nav/VirtueNavController.kt index ae11668..c9bf42b 100644 --- a/virtue-session/src/commonMain/kotlin/com/eygraber/virtue/session/nav/VirtueNavController.kt +++ b/virtue-session/src/commonMain/kotlin/com/eygraber/virtue/session/nav/VirtueNavController.kt @@ -222,8 +222,7 @@ internal class VirtueNavControllerImpl( override fun moveForward(): Boolean = history.canMoveForward.also { canMoveForward -> if(canMoveForward) { - // https://youtrack.jetbrains.com/issue/KT-70901 - should be fixed in 2.0.21 - history.isIgnoringPlatformChanges(true) + history.isIgnoringPlatformChanges = true val currentIndex = history.currentEntry.index navController.navigate(history[currentIndex + 1].route) history.move(1) @@ -328,8 +327,7 @@ internal class VirtueNavControllerImpl( } else { if(delta < 0) { - // https://youtrack.jetbrains.com/issue/KT-70901 - should be fixed in 2.0.21 - history.isIgnoringPlatformChanges(true) + history.isIgnoringPlatformChanges = true history.move(delta) } diff --git a/virtue-session/src/webMain/kotlin/com/eygraber/virtue/session/history/WebHistory.kt b/virtue-session/src/webMain/kotlin/com/eygraber/virtue/session/history/WebHistory.kt index d10fcf9..ec78ab9 100644 --- a/virtue-session/src/webMain/kotlin/com/eygraber/virtue/session/history/WebHistory.kt +++ b/virtue-session/src/webMain/kotlin/com/eygraber/virtue/session/history/WebHistory.kt @@ -34,10 +34,6 @@ internal class WebHistory( history.isIgnoringPlatformChanges = value } - override fun isIgnoringPlatformChanges(value: Boolean) { - history.isIgnoringPlatformChanges(value) - } - override fun get(index: Int): History.Entry = history[index] override fun push(route: VR): History.Entry = diff --git a/virtue-storage-kv/src/commonMain/kotlin/com/eygraber/virtue/storage/kv/PersistedKeyValueStorage.kt b/virtue-storage-kv/src/commonMain/kotlin/com/eygraber/virtue/storage/kv/PersistedKeyValueStorage.kt index 7f94f89..5c930a3 100644 --- a/virtue-storage-kv/src/commonMain/kotlin/com/eygraber/virtue/storage/kv/PersistedKeyValueStorage.kt +++ b/virtue-storage-kv/src/commonMain/kotlin/com/eygraber/virtue/storage/kv/PersistedKeyValueStorage.kt @@ -80,7 +80,7 @@ public class PersistedKeyValueStorage( private suspend inline fun awaitLoaded(block: () -> R): R { if(map.value === sentinel) { - map.value = store.get()?.associate { it.key to it.value } ?: emptyMap() + map.value = store.get()?.associate { it.key to it.value }.orEmpty() } return block() diff --git a/virtue-storage-kv/src/commonMain/kotlin/com/eygraber/virtue/storage/kv/VirtueKeyValueStorage.kt b/virtue-storage-kv/src/commonMain/kotlin/com/eygraber/virtue/storage/kv/VirtueKeyValueStorage.kt index bb1420d..5a0be3b 100644 --- a/virtue-storage-kv/src/commonMain/kotlin/com/eygraber/virtue/storage/kv/VirtueKeyValueStorage.kt +++ b/virtue-storage-kv/src/commonMain/kotlin/com/eygraber/virtue/storage/kv/VirtueKeyValueStorage.kt @@ -1,3 +1,5 @@ +@file:Suppress("MethodOverloading") + package com.eygraber.virtue.storage.kv import com.eygraber.virtue.storage.kv.VirtueKeyValueStorage.Editable diff --git a/virtue-storage-kv/src/commonMain/kotlin/com/eygraber/virtue/storage/kv/VirtueKeyValueStorageCleaner.kt b/virtue-storage-kv/src/commonMain/kotlin/com/eygraber/virtue/storage/kv/VirtueKeyValueStorageCleaner.kt index 3d930c5..01973d1 100644 --- a/virtue-storage-kv/src/commonMain/kotlin/com/eygraber/virtue/storage/kv/VirtueKeyValueStorageCleaner.kt +++ b/virtue-storage-kv/src/commonMain/kotlin/com/eygraber/virtue/storage/kv/VirtueKeyValueStorageCleaner.kt @@ -1,5 +1,6 @@ package com.eygraber.virtue.storage.kv +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import me.tatarka.inject.annotations.Inject @@ -11,8 +12,10 @@ public class VirtueKeyValueStorageCleaner( private val userKeyValueStorage: UserKeyValueStorage, private val encryptedUserKeyValueStorage: EncryptedUserKeyValueStorage, ) { - public suspend fun cleanStorage() { - withContext(Dispatchers.Default) { + public suspend fun cleanStorage( + dispatcher: CoroutineDispatcher = Dispatchers.Default, + ) { + withContext(dispatcher) { deviceKeyValueStorage.edit { clear() } encryptedDeviceKeyValueStorage.edit { clear() } userKeyValueStorage.edit { clear() } diff --git a/virtue-theme/build.gradle.kts b/virtue-theme/build.gradle.kts index 608a821..d232ecf 100644 --- a/virtue-theme/build.gradle.kts +++ b/virtue-theme/build.gradle.kts @@ -27,5 +27,9 @@ kotlin { jvmMain.dependencies { implementation(projects.virtuePaths) } + + wasmJsMain.dependencies { + implementation(libs.kotlinx.wasm.browser) + } } }