diff --git a/.github/labeler.yml b/.github/labeler.yml
index f596b56f3..09f5ba210 100644
--- a/.github/labeler.yml
+++ b/.github/labeler.yml
@@ -16,7 +16,7 @@ postgrest:
auth:
- changed-files:
- - any-glob-to-any-file: GoTrue/src/**
+ - any-glob-to-any-file: Auth/src/**
storage:
- changed-files:
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index c29ae48f3..d1441205d 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -21,9 +21,9 @@ jobs:
java-version: '17'
distribution: 'temurin'
- name: Setup Gradle
- uses: gradle/actions/setup-gradle@v3.1.0
+ uses: gradle/actions/setup-gradle@v4.0.1
with:
cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
cache-read-only: false
- name: Build supabase-kt
- run: ./gradlew -DLibrariesOnly=true build --stacktrace --configuration-cache --scan
\ No newline at end of file
+ run: ./gradlew -DLibrariesOnly=true build -x test --stacktrace --configuration-cache --scan
diff --git a/.github/workflows/cache.yml b/.github/workflows/cache.yml
new file mode 100644
index 000000000..02e144952
--- /dev/null
+++ b/.github/workflows/cache.yml
@@ -0,0 +1,16 @@
+name: Clear all Github actions caches
+on:
+ workflow_dispatch:
+
+permissions:
+ actions: write
+
+jobs:
+ clear-cache:
+ name: Delete all caches
+ runs-on: ubuntu-20.04
+ steps:
+ - name: Clear caches
+ uses: easimon/wipe-cache@main
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
\ No newline at end of file
diff --git a/.github/workflows/detekt.yml b/.github/workflows/detekt.yml
index e8150ca08..9026982e7 100644
--- a/.github/workflows/detekt.yml
+++ b/.github/workflows/detekt.yml
@@ -20,9 +20,14 @@ jobs:
java-version: '17'
distribution: 'temurin'
- name: Setup Gradle
- uses: gradle/actions/setup-gradle@v3.1.0
+ uses: gradle/actions/setup-gradle@v4.0.1
with:
cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
cache-read-only: ${{ github.ref != 'refs/heads/master' }}
- name: Analyze code using detekt
- run: ./gradlew -DLibrariesOnly=true detektAll --stacktrace --configuration-cache
\ No newline at end of file
+ run: ./gradlew -DLibrariesOnly=true detektAll --stacktrace --configuration-cache
+ - uses: github/codeql-action/upload-sarif@v3
+ if: success() || failure()
+ with:
+ sarif_file: build/reports/detekt/merge.sarif
+ category: lint
\ No newline at end of file
diff --git a/.github/workflows/dokka.yml b/.github/workflows/dokka.yml
index 88d9bea5f..dd02a0ad0 100644
--- a/.github/workflows/dokka.yml
+++ b/.github/workflows/dokka.yml
@@ -38,7 +38,7 @@ jobs:
java-version: '17'
distribution: 'temurin'
- name: Setup Gradle
- uses: gradle/actions/setup-gradle@v3.1.0
+ uses: gradle/actions/setup-gradle@v4.0.1
with:
cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
cache-read-only: true
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index fdc44c5b4..ebc2c76dd 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -27,7 +27,7 @@ jobs:
java-version: '17'
distribution: 'temurin'
- name: Setup Gradle
- uses: gradle/actions/setup-gradle@v3.1.0
+ uses: gradle/actions/setup-gradle@v4.0.1
with:
cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
cache-read-only: true
diff --git a/.github/workflows/samples.yml b/.github/workflows/samples.yml
index 8b291ee9b..1af0f2f09 100644
--- a/.github/workflows/samples.yml
+++ b/.github/workflows/samples.yml
@@ -27,7 +27,7 @@ jobs:
java-version: '17'
distribution: 'temurin'
- name: Setup Gradle
- uses: gradle/actions/setup-gradle@v3.1.0
+ uses: gradle/actions/setup-gradle@v4.0.1
with:
cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
cache-read-only: ${{ github.ref != 'refs/heads/master' }}
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 9f7fb3e09..3c4a06d67 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -14,7 +14,7 @@ jobs:
matrix:
command: [
'jvmTest testDebugUnitTest testReleaseUnitTest',
- 'jsTest',
+ 'jsTest wasmJsTest',
'iosX64Test iosSimulatorArm64Test',
'macosArm64Test macosX64Test',
'tvosX64Test tvosSimulatorArm64Test',
@@ -31,7 +31,7 @@ jobs:
java-version: '17'
distribution: 'temurin'
- name: Setup Gradle
- uses: gradle/actions/setup-gradle@v3.1.0
+ uses: gradle/actions/setup-gradle@v4.0.1
with:
cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
cache-read-only: ${{ github.ref != 'refs/heads/master' }}
diff --git a/.gitignore b/.gitignore
index b00cb7e52..419017108 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,5 @@
# Project exclude paths
-/.gradle/
+**/.gradle/**
**/build/**
/test/
.idea
diff --git a/Auth/README.md b/Auth/README.md
new file mode 100644
index 000000000..b6dcd8120
--- /dev/null
+++ b/Auth/README.md
@@ -0,0 +1,70 @@
+# Supabase-kt Auth
+
+**Only available for versions 3.0.0 and above. For versions below 3.0.0, checkout the [old README](/GoTrue)**
+
+Extends Supabase-kt with a multiplatform Auth client.
+
+Supported targets:
+
+| Target | **JVM** | **Android** | **JS** | **Wasm** | **Apple** | **Windows** | **Linux** |
+|--------|---------|-------------|--------|----------|-----------|-------------|-----------|
+| Status | ✅ | ✅ | ✅ | ✅ | ☑️* | ☑️ | ☑️ |
+
+> ☑️ = No built-in OAuth support. Linux has no support for persistent session storage.
+
+\* **iOS and macOS are fully supported**
+
+
+
+In-depth Kotlin targets
+
+**JS**: Browser, NodeJS
+
+**Wasm**: wasm-js
+
+**Apple:**
+
+- iOS: iosArm64, iosSimulatorArm64, iosX64
+
+- tvOS: tvosArm64, tvosX64, tvosSimulatorArm64
+
+- watchOS: watchosArm64, watchosX64, watchosSimulatorArm64
+
+- MacOS: macosX64, macosArm64
+
+**Windows**: mingwX64
+
+**Linux**: linuxX64
+
+
+
+# Installation
+
+Newest version: [![](https://img.shields.io/github/release/supabase-community/supabase-kt?label=)](https://github.com/supabase-community/supabase-kt/releases)
+
+```kotlin
+dependencies {
+ implementation("io.github.jan-tennert.supabase:auth-kt:VERSION")
+}
+```
+
+Install the plugin in your SupabaseClient. See the [documentation](https://supabase.com/docs/reference/kotlin/initializing) for more information
+
+```kotlin
+val supabase = createSupabaseClient(
+ supabaseUrl = "https://id.supabase.co",
+ supabaseKey = "apikey"
+) {
+
+ //...
+
+ install(Auth) {
+ // settings
+ }
+
+}
+```
+
+# Usage
+
+See [Auth documentation](https://supabase.com/docs/reference/kotlin/auth-signup) for usage
diff --git a/GoTrue/build.gradle.kts b/Auth/build.gradle.kts
similarity index 98%
rename from GoTrue/build.gradle.kts
rename to Auth/build.gradle.kts
index 354dea6e3..971f4c78a 100644
--- a/GoTrue/build.gradle.kts
+++ b/Auth/build.gradle.kts
@@ -29,6 +29,7 @@ kotlin {
withWatchos()
withMingw()
withJs()
+ withWasmJs()
}
}
}
diff --git a/GoTrue/src/androidMain/AndroidManifest.xml b/Auth/src/androidMain/AndroidManifest.xml
similarity index 86%
rename from GoTrue/src/androidMain/AndroidManifest.xml
rename to Auth/src/androidMain/AndroidManifest.xml
index bc8472a06..088c82e26 100644
--- a/GoTrue/src/androidMain/AndroidManifest.xml
+++ b/Auth/src/androidMain/AndroidManifest.xml
@@ -9,7 +9,7 @@
android:exported="false"
tools:node="merge">
diff --git a/GoTrue/src/androidMain/kotlin/io/github/jan/supabase/gotrue/Android.kt b/Auth/src/androidMain/kotlin/io/github/jan/supabase/auth/Android.kt
similarity index 95%
rename from GoTrue/src/androidMain/kotlin/io/github/jan/supabase/gotrue/Android.kt
rename to Auth/src/androidMain/kotlin/io/github/jan/supabase/auth/Android.kt
index 004e0b729..c1378f8ab 100644
--- a/GoTrue/src/androidMain/kotlin/io/github/jan/supabase/gotrue/Android.kt
+++ b/Auth/src/androidMain/kotlin/io/github/jan/supabase/auth/Android.kt
@@ -1,11 +1,11 @@
-package io.github.jan.supabase.gotrue
+package io.github.jan.supabase.auth
import android.content.Intent
import android.net.Uri
import androidx.browser.customtabs.CustomTabsIntent
import io.github.jan.supabase.SupabaseClient
import io.github.jan.supabase.annotations.SupabaseInternal
-import io.github.jan.supabase.gotrue.user.UserSession
+import io.github.jan.supabase.auth.user.UserSession
import kotlinx.coroutines.launch
internal fun openUrl(uri: Uri, action: ExternalAuthAction) {
diff --git a/GoTrue/src/androidMain/kotlin/io/github/jan/supabase/gotrue/AuthConfig.kt b/Auth/src/androidMain/kotlin/io/github/jan/supabase/auth/AuthConfig.kt
similarity index 64%
rename from GoTrue/src/androidMain/kotlin/io/github/jan/supabase/gotrue/AuthConfig.kt
rename to Auth/src/androidMain/kotlin/io/github/jan/supabase/auth/AuthConfig.kt
index 67d9fb384..e20e9fec3 100644
--- a/GoTrue/src/androidMain/kotlin/io/github/jan/supabase/gotrue/AuthConfig.kt
+++ b/Auth/src/androidMain/kotlin/io/github/jan/supabase/auth/AuthConfig.kt
@@ -1,9 +1,7 @@
-package io.github.jan.supabase.gotrue
+package io.github.jan.supabase.auth
import androidx.browser.customtabs.CustomTabsIntent
-import io.github.jan.supabase.gotrue.ExternalAuthAction.Companion.CUSTOM_TABS
-import io.github.jan.supabase.gotrue.ExternalAuthAction.Companion.EXTERNAL_BROWSER
-import io.github.jan.supabase.gotrue.providers.ExternalAuthConfig
+import io.github.jan.supabase.auth.providers.ExternalAuthConfig
import io.github.jan.supabase.plugins.CustomSerializationConfig
/**
@@ -38,22 +36,12 @@ sealed interface ExternalAuthAction {
data class CustomTabs(val intentBuilder: CustomTabsIntent.Builder.() -> Unit = {}) : ExternalAuthAction
companion object {
+
/**
* The default action to use for the OAuth flow
*/
val DEFAULT: ExternalAuthAction = ExternalBrowser
- /**
- * External browser action
- */
- @Deprecated("Use ExternalBrowser object instead", ReplaceWith("ExternalAuthAction.ExternalBrowser"))
- val EXTERNAL_BROWSER: ExternalAuthAction = ExternalBrowser
-
- /**
- * Custom tabs action
- */
- @Deprecated("Use CustomTabs class instead", ReplaceWith("ExternalAuthAction.CustomTabs()"))
- val CUSTOM_TABS: ExternalAuthAction = CustomTabs()
}
}
diff --git a/GoTrue/src/androidMain/kotlin/io/github/jan/supabase/gotrue/RedirectUrl.android.kt b/Auth/src/androidMain/kotlin/io/github/jan/supabase/auth/RedirectUrl.android.kt
similarity index 81%
rename from GoTrue/src/androidMain/kotlin/io/github/jan/supabase/gotrue/RedirectUrl.android.kt
rename to Auth/src/androidMain/kotlin/io/github/jan/supabase/auth/RedirectUrl.android.kt
index 9ddc4717e..2c8887fef 100644
--- a/GoTrue/src/androidMain/kotlin/io/github/jan/supabase/gotrue/RedirectUrl.android.kt
+++ b/Auth/src/androidMain/kotlin/io/github/jan/supabase/auth/RedirectUrl.android.kt
@@ -1,4 +1,4 @@
-package io.github.jan.supabase.gotrue
+package io.github.jan.supabase.auth
import io.github.jan.supabase.annotations.SupabaseInternal
diff --git a/GoTrue/src/androidMain/kotlin/io/github/jan/supabase/gotrue/Utils.android.kt b/Auth/src/androidMain/kotlin/io/github/jan/supabase/auth/Utils.android.kt
similarity index 84%
rename from GoTrue/src/androidMain/kotlin/io/github/jan/supabase/gotrue/Utils.android.kt
rename to Auth/src/androidMain/kotlin/io/github/jan/supabase/auth/Utils.android.kt
index 0a1b89cde..fa42ca949 100644
--- a/GoTrue/src/androidMain/kotlin/io/github/jan/supabase/gotrue/Utils.android.kt
+++ b/Auth/src/androidMain/kotlin/io/github/jan/supabase/auth/Utils.android.kt
@@ -1,9 +1,9 @@
@file:Suppress("RedundantSuspendModifier")
-package io.github.jan.supabase.gotrue
+package io.github.jan.supabase.auth
import android.net.Uri
import io.github.jan.supabase.SupabaseClient
-import io.github.jan.supabase.gotrue.user.UserSession
+import io.github.jan.supabase.auth.user.UserSession
internal actual suspend fun SupabaseClient.openExternalUrl(url: String) {
openUrl(Uri.parse(url), auth.config.defaultExternalAuthAction)
diff --git a/GoTrue/src/jvmMain/kotlin/io/github/jan/supabase/gotrue/providers/ExternalAuthConfig.kt b/Auth/src/androidMain/kotlin/io/github/jan/supabase/auth/providers/ExternalAuthConfig.kt
similarity index 76%
rename from GoTrue/src/jvmMain/kotlin/io/github/jan/supabase/gotrue/providers/ExternalAuthConfig.kt
rename to Auth/src/androidMain/kotlin/io/github/jan/supabase/auth/providers/ExternalAuthConfig.kt
index de8a14b38..267e6bf68 100644
--- a/GoTrue/src/jvmMain/kotlin/io/github/jan/supabase/gotrue/providers/ExternalAuthConfig.kt
+++ b/Auth/src/androidMain/kotlin/io/github/jan/supabase/auth/providers/ExternalAuthConfig.kt
@@ -1,4 +1,4 @@
-package io.github.jan.supabase.gotrue.providers
+package io.github.jan.supabase.auth.providers
/**
* Configuration for external authentication providers like Google, Twitter, etc.
diff --git a/GoTrue/src/androidMain/kotlin/io/github/jan/supabase/gotrue/setupPlatform.kt b/Auth/src/androidMain/kotlin/io/github/jan/supabase/auth/setupPlatform.kt
similarity index 98%
rename from GoTrue/src/androidMain/kotlin/io/github/jan/supabase/gotrue/setupPlatform.kt
rename to Auth/src/androidMain/kotlin/io/github/jan/supabase/auth/setupPlatform.kt
index e14b0c8d4..458a00b36 100644
--- a/GoTrue/src/androidMain/kotlin/io/github/jan/supabase/gotrue/setupPlatform.kt
+++ b/Auth/src/androidMain/kotlin/io/github/jan/supabase/auth/setupPlatform.kt
@@ -1,4 +1,4 @@
-package io.github.jan.supabase.gotrue
+package io.github.jan.supabase.auth
import android.content.Context
import androidx.lifecycle.DefaultLifecycleObserver
diff --git a/GoTrue/src/androidUnitTest/kotlin/platformSettings.kt b/Auth/src/androidUnitTest/kotlin/platformSettings.kt
similarity index 63%
rename from GoTrue/src/androidUnitTest/kotlin/platformSettings.kt
rename to Auth/src/androidUnitTest/kotlin/platformSettings.kt
index 2386095b9..687460611 100644
--- a/GoTrue/src/androidUnitTest/kotlin/platformSettings.kt
+++ b/Auth/src/androidUnitTest/kotlin/platformSettings.kt
@@ -1,4 +1,4 @@
-import io.github.jan.supabase.gotrue.AuthConfig
+import io.github.jan.supabase.auth.AuthConfig
actual fun AuthConfig.platformSettings() {
enableLifecycleCallbacks = false
diff --git a/GoTrue/src/appleMain/kotlin/io/github/jan/supabase/gotrue/Apple.kt b/Auth/src/appleMain/kotlin/io/github/jan/supabase/auth/Apple.kt
similarity index 94%
rename from GoTrue/src/appleMain/kotlin/io/github/jan/supabase/gotrue/Apple.kt
rename to Auth/src/appleMain/kotlin/io/github/jan/supabase/auth/Apple.kt
index 063aad94e..c83e19a43 100644
--- a/GoTrue/src/appleMain/kotlin/io/github/jan/supabase/gotrue/Apple.kt
+++ b/Auth/src/appleMain/kotlin/io/github/jan/supabase/auth/Apple.kt
@@ -1,7 +1,7 @@
-package io.github.jan.supabase.gotrue
+package io.github.jan.supabase.auth
import io.github.jan.supabase.SupabaseClient
-import io.github.jan.supabase.gotrue.user.UserSession
+import io.github.jan.supabase.auth.user.UserSession
import io.github.jan.supabase.logging.d
import kotlinx.coroutines.launch
import platform.Foundation.NSURL
diff --git a/GoTrue/src/appleMain/kotlin/io/github/jan/supabase/gotrue/RedirectUrl.apple.kt b/Auth/src/appleMain/kotlin/io/github/jan/supabase/auth/RedirectUrl.apple.kt
similarity index 81%
rename from GoTrue/src/appleMain/kotlin/io/github/jan/supabase/gotrue/RedirectUrl.apple.kt
rename to Auth/src/appleMain/kotlin/io/github/jan/supabase/auth/RedirectUrl.apple.kt
index 9ddc4717e..2c8887fef 100644
--- a/GoTrue/src/appleMain/kotlin/io/github/jan/supabase/gotrue/RedirectUrl.apple.kt
+++ b/Auth/src/appleMain/kotlin/io/github/jan/supabase/auth/RedirectUrl.apple.kt
@@ -1,4 +1,4 @@
-package io.github.jan.supabase.gotrue
+package io.github.jan.supabase.auth
import io.github.jan.supabase.annotations.SupabaseInternal
diff --git a/GoTrue/src/appleMain/kotlin/io/github/jan/supabase/gotrue/Utils.apple.kt b/Auth/src/appleMain/kotlin/io/github/jan/supabase/auth/Utils.apple.kt
similarity index 66%
rename from GoTrue/src/appleMain/kotlin/io/github/jan/supabase/gotrue/Utils.apple.kt
rename to Auth/src/appleMain/kotlin/io/github/jan/supabase/auth/Utils.apple.kt
index af0b81ecd..38015ada0 100644
--- a/GoTrue/src/appleMain/kotlin/io/github/jan/supabase/gotrue/Utils.apple.kt
+++ b/Auth/src/appleMain/kotlin/io/github/jan/supabase/auth/Utils.apple.kt
@@ -1,7 +1,7 @@
-package io.github.jan.supabase.gotrue
+package io.github.jan.supabase.auth
import io.github.jan.supabase.SupabaseClient
-import io.github.jan.supabase.gotrue.providers.openUrl
+import io.github.jan.supabase.auth.providers.openUrl
import platform.Foundation.NSURL
internal actual suspend fun SupabaseClient.openExternalUrl(url: String) {
diff --git a/GoTrue/src/linuxMain/kotlin/io/github/jan/supabase/gotrue/providers/ExternalAuthConfig.kt b/Auth/src/appleMain/kotlin/io/github/jan/supabase/auth/providers/ExternalAuthConfig.kt
similarity index 76%
rename from GoTrue/src/linuxMain/kotlin/io/github/jan/supabase/gotrue/providers/ExternalAuthConfig.kt
rename to Auth/src/appleMain/kotlin/io/github/jan/supabase/auth/providers/ExternalAuthConfig.kt
index de8a14b38..267e6bf68 100644
--- a/GoTrue/src/linuxMain/kotlin/io/github/jan/supabase/gotrue/providers/ExternalAuthConfig.kt
+++ b/Auth/src/appleMain/kotlin/io/github/jan/supabase/auth/providers/ExternalAuthConfig.kt
@@ -1,4 +1,4 @@
-package io.github.jan.supabase.gotrue.providers
+package io.github.jan.supabase.auth.providers
/**
* Configuration for external authentication providers like Google, Twitter, etc.
diff --git a/GoTrue/src/appleMain/kotlin/io/github/jan/supabase/gotrue/providers/OAuthProvider.kt b/Auth/src/appleMain/kotlin/io/github/jan/supabase/auth/providers/OAuthProvider.kt
similarity index 60%
rename from GoTrue/src/appleMain/kotlin/io/github/jan/supabase/gotrue/providers/OAuthProvider.kt
rename to Auth/src/appleMain/kotlin/io/github/jan/supabase/auth/providers/OAuthProvider.kt
index cabdc4940..a6f9c1424 100644
--- a/GoTrue/src/appleMain/kotlin/io/github/jan/supabase/gotrue/providers/OAuthProvider.kt
+++ b/Auth/src/appleMain/kotlin/io/github/jan/supabase/auth/providers/OAuthProvider.kt
@@ -1,4 +1,4 @@
-package io.github.jan.supabase.gotrue.providers
+package io.github.jan.supabase.auth.providers
import platform.Foundation.NSURL
diff --git a/GoTrue/src/jvmMain/kotlin/io/github/jan/supabase/gotrue/setupPlatform.kt b/Auth/src/appleMain/kotlin/io/github/jan/supabase/auth/setupPlatform.kt
similarity index 75%
rename from GoTrue/src/jvmMain/kotlin/io/github/jan/supabase/gotrue/setupPlatform.kt
rename to Auth/src/appleMain/kotlin/io/github/jan/supabase/auth/setupPlatform.kt
index 95b106316..a1b155557 100644
--- a/GoTrue/src/jvmMain/kotlin/io/github/jan/supabase/gotrue/setupPlatform.kt
+++ b/Auth/src/appleMain/kotlin/io/github/jan/supabase/auth/setupPlatform.kt
@@ -1,4 +1,4 @@
-package io.github.jan.supabase.gotrue
+package io.github.jan.supabase.auth
import io.github.jan.supabase.annotations.SupabaseInternal
diff --git a/GoTrue/src/jvmTest/kotlin/platformSettings.kt b/Auth/src/appleTest/kotlin/platformSettings.kt
similarity index 50%
rename from GoTrue/src/jvmTest/kotlin/platformSettings.kt
rename to Auth/src/appleTest/kotlin/platformSettings.kt
index c97cb2bd9..f6fa8ceb8 100644
--- a/GoTrue/src/jvmTest/kotlin/platformSettings.kt
+++ b/Auth/src/appleTest/kotlin/platformSettings.kt
@@ -1,3 +1,3 @@
-import io.github.jan.supabase.gotrue.AuthConfig
+import io.github.jan.supabase.auth.AuthConfig
actual fun AuthConfig.platformSettings() = Unit
\ No newline at end of file
diff --git a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/AccessToken.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/AccessToken.kt
similarity index 97%
rename from GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/AccessToken.kt
rename to Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/AccessToken.kt
index 0a3a4a537..ed2a5e6ce 100644
--- a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/AccessToken.kt
+++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/AccessToken.kt
@@ -1,4 +1,4 @@
-package io.github.jan.supabase.gotrue
+package io.github.jan.supabase.auth
import io.github.jan.supabase.SupabaseClient
import io.github.jan.supabase.annotations.SupabaseInternal
diff --git a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/Auth.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/Auth.kt
similarity index 92%
rename from GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/Auth.kt
rename to Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/Auth.kt
index 3ed62db8b..d7c1ad003 100644
--- a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/Auth.kt
+++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/Auth.kt
@@ -1,22 +1,22 @@
-package io.github.jan.supabase.gotrue
+package io.github.jan.supabase.auth
import io.github.jan.supabase.SupabaseClient
+import io.github.jan.supabase.auth.admin.AdminApi
+import io.github.jan.supabase.auth.exception.AuthRestException
+import io.github.jan.supabase.auth.exception.AuthWeakPasswordException
+import io.github.jan.supabase.auth.mfa.MfaApi
+import io.github.jan.supabase.auth.providers.AuthProvider
+import io.github.jan.supabase.auth.providers.ExternalAuthConfigDefaults
+import io.github.jan.supabase.auth.providers.Google
+import io.github.jan.supabase.auth.providers.OAuthProvider
+import io.github.jan.supabase.auth.providers.builtin.Email
+import io.github.jan.supabase.auth.providers.builtin.Phone
+import io.github.jan.supabase.auth.providers.builtin.SSO
+import io.github.jan.supabase.auth.user.UserInfo
+import io.github.jan.supabase.auth.user.UserSession
+import io.github.jan.supabase.auth.user.UserUpdateBuilder
import io.github.jan.supabase.exceptions.HttpRequestException
import io.github.jan.supabase.exceptions.RestException
-import io.github.jan.supabase.gotrue.admin.AdminApi
-import io.github.jan.supabase.gotrue.exception.AuthRestException
-import io.github.jan.supabase.gotrue.exception.AuthWeakPasswordException
-import io.github.jan.supabase.gotrue.mfa.MfaApi
-import io.github.jan.supabase.gotrue.providers.AuthProvider
-import io.github.jan.supabase.gotrue.providers.ExternalAuthConfigDefaults
-import io.github.jan.supabase.gotrue.providers.Google
-import io.github.jan.supabase.gotrue.providers.OAuthProvider
-import io.github.jan.supabase.gotrue.providers.builtin.Email
-import io.github.jan.supabase.gotrue.providers.builtin.Phone
-import io.github.jan.supabase.gotrue.providers.builtin.SSO
-import io.github.jan.supabase.gotrue.user.UserInfo
-import io.github.jan.supabase.gotrue.user.UserSession
-import io.github.jan.supabase.gotrue.user.UserUpdateBuilder
import io.github.jan.supabase.logging.SupabaseLogger
import io.github.jan.supabase.logging.e
import io.github.jan.supabase.plugins.CustomSerializationPlugin
@@ -202,21 +202,6 @@ sealed interface Auth : MainPlugin, CustomSerializationPlugin {
config: UserUpdateBuilder.() -> Unit
): UserInfo
- /**
- * Modifies the current user
- * @param updateCurrentUser Whether to update the current user in the [SupabaseClient]
- * @param config The configuration to use
- * @throws RestException or one of its subclasses if receiving an error response. If the error response contains a error code, an [AuthRestException] will be thrown which can be used to easier identify the problem.
- * @throws HttpRequestTimeoutException if the request timed out
- * @throws HttpRequestException on network related issues
- */
- @Deprecated("Use updateUser instead")
- suspend fun modifyUser(
- updateCurrentUser: Boolean = true,
- redirectUrl: String? = defaultRedirectUrl(),
- config: UserUpdateBuilder.() -> Unit
- ): UserInfo = updateUser(updateCurrentUser, redirectUrl, config)
-
/**
* Resends an existing signup confirmation email, email change email
* @param type The email otp type
diff --git a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/AuthConfig.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/AuthConfig.kt
similarity index 98%
rename from GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/AuthConfig.kt
rename to Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/AuthConfig.kt
index f812e85f1..f0b3c6b61 100644
--- a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/AuthConfig.kt
+++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/AuthConfig.kt
@@ -1,4 +1,4 @@
-package io.github.jan.supabase.gotrue
+package io.github.jan.supabase.auth
import io.github.jan.supabase.SupabaseClientBuilder
import io.github.jan.supabase.SupabaseSerializer
@@ -147,6 +147,7 @@ val AuthConfig.deepLinkOrNull: String?
* @param enableLifecycleCallbacks Whether to stop auto-refresh on focus loss, and resume it on focus again. Currently only supported on Android.
* @see AuthConfigDefaults
*/
+@Suppress("LongParameterList")
fun AuthConfigDefaults.minimalSettings(
alwaysAutoRefresh: Boolean = false,
autoLoadFromStorage: Boolean = false,
diff --git a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/AuthExtensions.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/AuthExtensions.kt
similarity index 96%
rename from GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/AuthExtensions.kt
rename to Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/AuthExtensions.kt
index 91f89af50..fb589bd75 100644
--- a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/AuthExtensions.kt
+++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/AuthExtensions.kt
@@ -1,7 +1,7 @@
-package io.github.jan.supabase.gotrue
+package io.github.jan.supabase.auth
+import io.github.jan.supabase.auth.user.UserSession
import io.github.jan.supabase.encodeToJsonElement
-import io.github.jan.supabase.gotrue.user.UserSession
import io.github.jan.supabase.logging.d
import kotlinx.serialization.json.jsonObject
diff --git a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/AuthImpl.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/AuthImpl.kt
similarity index 96%
rename from GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/AuthImpl.kt
rename to Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/AuthImpl.kt
index f8cecbdcd..b7fa0ec53 100644
--- a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/AuthImpl.kt
+++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/AuthImpl.kt
@@ -1,28 +1,28 @@
-package io.github.jan.supabase.gotrue
+package io.github.jan.supabase.auth
import io.github.jan.supabase.SupabaseClient
import io.github.jan.supabase.annotations.SupabaseExperimental
import io.github.jan.supabase.annotations.SupabaseInternal
+import io.github.jan.supabase.auth.admin.AdminApi
+import io.github.jan.supabase.auth.admin.AdminApiImpl
+import io.github.jan.supabase.auth.exception.AuthRestException
+import io.github.jan.supabase.auth.exception.AuthSessionMissingException
+import io.github.jan.supabase.auth.exception.AuthWeakPasswordException
+import io.github.jan.supabase.auth.mfa.MfaApi
+import io.github.jan.supabase.auth.mfa.MfaApiImpl
+import io.github.jan.supabase.auth.providers.AuthProvider
+import io.github.jan.supabase.auth.providers.ExternalAuthConfigDefaults
+import io.github.jan.supabase.auth.providers.OAuthProvider
+import io.github.jan.supabase.auth.providers.builtin.OTP
+import io.github.jan.supabase.auth.providers.builtin.SSO
+import io.github.jan.supabase.auth.user.UserInfo
+import io.github.jan.supabase.auth.user.UserSession
+import io.github.jan.supabase.auth.user.UserUpdateBuilder
import io.github.jan.supabase.bodyOrNull
import io.github.jan.supabase.exceptions.BadRequestRestException
import io.github.jan.supabase.exceptions.RestException
import io.github.jan.supabase.exceptions.UnauthorizedRestException
import io.github.jan.supabase.exceptions.UnknownRestException
-import io.github.jan.supabase.gotrue.admin.AdminApi
-import io.github.jan.supabase.gotrue.admin.AdminApiImpl
-import io.github.jan.supabase.gotrue.exception.AuthRestException
-import io.github.jan.supabase.gotrue.exception.AuthSessionMissingException
-import io.github.jan.supabase.gotrue.exception.AuthWeakPasswordException
-import io.github.jan.supabase.gotrue.mfa.MfaApi
-import io.github.jan.supabase.gotrue.mfa.MfaApiImpl
-import io.github.jan.supabase.gotrue.providers.AuthProvider
-import io.github.jan.supabase.gotrue.providers.ExternalAuthConfigDefaults
-import io.github.jan.supabase.gotrue.providers.OAuthProvider
-import io.github.jan.supabase.gotrue.providers.builtin.OTP
-import io.github.jan.supabase.gotrue.providers.builtin.SSO
-import io.github.jan.supabase.gotrue.user.UserInfo
-import io.github.jan.supabase.gotrue.user.UserSession
-import io.github.jan.supabase.gotrue.user.UserUpdateBuilder
import io.github.jan.supabase.logging.d
import io.github.jan.supabase.logging.e
import io.github.jan.supabase.logging.i
diff --git a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/AuthenticatedSupabaseApi.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/AuthenticatedSupabaseApi.kt
similarity index 98%
rename from GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/AuthenticatedSupabaseApi.kt
rename to Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/AuthenticatedSupabaseApi.kt
index 424afc341..aca2fd113 100644
--- a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/AuthenticatedSupabaseApi.kt
+++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/AuthenticatedSupabaseApi.kt
@@ -1,5 +1,5 @@
@file:Suppress("UndocumentedPublicClass", "UndocumentedPublicFunction")
-package io.github.jan.supabase.gotrue
+package io.github.jan.supabase.auth
import io.github.jan.supabase.SupabaseClient
import io.github.jan.supabase.annotations.SupabaseInternal
diff --git a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/CodeVerifierCache.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/CodeVerifierCache.kt
similarity index 96%
rename from GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/CodeVerifierCache.kt
rename to Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/CodeVerifierCache.kt
index 8f1d32278..8a1cbff6e 100644
--- a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/CodeVerifierCache.kt
+++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/CodeVerifierCache.kt
@@ -1,4 +1,4 @@
-package io.github.jan.supabase.gotrue
+package io.github.jan.supabase.auth
import kotlinx.atomicfu.AtomicRef
import kotlinx.atomicfu.atomic
diff --git a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/GoTrueErrorResponse.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/GoTrueErrorResponse.kt
similarity index 98%
rename from GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/GoTrueErrorResponse.kt
rename to Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/GoTrueErrorResponse.kt
index 101d8034f..83d8381da 100644
--- a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/GoTrueErrorResponse.kt
+++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/GoTrueErrorResponse.kt
@@ -1,4 +1,4 @@
-package io.github.jan.supabase.gotrue
+package io.github.jan.supabase.auth
import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializable
diff --git a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/JsonUtils.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/JsonUtils.kt
similarity index 92%
rename from GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/JsonUtils.kt
rename to Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/JsonUtils.kt
index 588c84a31..82a4b12d3 100644
--- a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/JsonUtils.kt
+++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/JsonUtils.kt
@@ -1,4 +1,4 @@
-package io.github.jan.supabase.gotrue
+package io.github.jan.supabase.auth
import kotlinx.serialization.json.JsonObjectBuilder
import kotlinx.serialization.json.put
diff --git a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/OtpType.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/OtpType.kt
similarity index 90%
rename from GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/OtpType.kt
rename to Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/OtpType.kt
index c59bf64f1..dbda5b060 100644
--- a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/OtpType.kt
+++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/OtpType.kt
@@ -1,4 +1,4 @@
-package io.github.jan.supabase.gotrue
+package io.github.jan.supabase.auth
/**
* OTPs (One Time Passwords) are used to authenticate users via email or phone.
@@ -54,7 +54,14 @@ sealed interface OtpType {
* Phone OTP types
*/
enum class Phone(override val type: String): OtpType {
+ /**
+ * OTP type for sms messages
+ */
SMS("sms"),
+
+ /**
+ * OTP type for phone change flows
+ */
PHONE_CHANGE("phone_change")
}
diff --git a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/PKCE.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/PKCE.kt
similarity index 95%
rename from GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/PKCE.kt
rename to Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/PKCE.kt
index 3ef700bd4..76b138fbf 100644
--- a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/PKCE.kt
+++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/PKCE.kt
@@ -1,5 +1,5 @@
@file:Suppress("MatchingDeclarationName")
-package io.github.jan.supabase.gotrue
+package io.github.jan.supabase.auth
import korlibs.crypto.SHA256
import korlibs.crypto.SecureRandom
diff --git a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/PostgrestFilterDSL.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/PostgrestFilterDSL.kt
similarity index 82%
rename from GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/PostgrestFilterDSL.kt
rename to Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/PostgrestFilterDSL.kt
index 8f40ed9ce..981352837 100644
--- a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/PostgrestFilterDSL.kt
+++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/PostgrestFilterDSL.kt
@@ -1,4 +1,4 @@
-package io.github.jan.supabase.gotrue
+package io.github.jan.supabase.auth
/**
* Used to mark Postgrest filter DSL functions
diff --git a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/RedirectUrl.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/RedirectUrl.kt
similarity index 87%
rename from GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/RedirectUrl.kt
rename to Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/RedirectUrl.kt
index 40b794cef..80ac1c1d7 100644
--- a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/RedirectUrl.kt
+++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/RedirectUrl.kt
@@ -1,4 +1,4 @@
-package io.github.jan.supabase.gotrue
+package io.github.jan.supabase.auth
import io.github.jan.supabase.annotations.SupabaseInternal
diff --git a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/SessionManager.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/SessionManager.kt
similarity index 91%
rename from GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/SessionManager.kt
rename to Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/SessionManager.kt
index 743543853..14703dccc 100644
--- a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/SessionManager.kt
+++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/SessionManager.kt
@@ -1,6 +1,6 @@
-package io.github.jan.supabase.gotrue
+package io.github.jan.supabase.auth
-import io.github.jan.supabase.gotrue.user.UserSession
+import io.github.jan.supabase.auth.user.UserSession
import kotlinx.atomicfu.AtomicRef
import kotlinx.atomicfu.atomic
diff --git a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/SessionStatus.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/SessionStatus.kt
similarity index 95%
rename from GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/SessionStatus.kt
rename to Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/SessionStatus.kt
index 0e2e2dd6e..73e04f6dd 100644
--- a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/SessionStatus.kt
+++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/SessionStatus.kt
@@ -1,7 +1,7 @@
-package io.github.jan.supabase.gotrue
+package io.github.jan.supabase.auth
-import io.github.jan.supabase.gotrue.providers.AuthProvider
-import io.github.jan.supabase.gotrue.user.UserSession
+import io.github.jan.supabase.auth.providers.AuthProvider
+import io.github.jan.supabase.auth.user.UserSession
/**
* Represents the status of the current session in [Auth]
diff --git a/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/SignOutScope.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/SignOutScope.kt
new file mode 100644
index 000000000..5992199e2
--- /dev/null
+++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/SignOutScope.kt
@@ -0,0 +1,23 @@
+package io.github.jan.supabase.auth
+
+/**
+ * Represents the scope of a sign-out action.
+ *
+ * The sign-out scope determines the scope of the sign-out action being performed.
+ */
+enum class SignOutScope {
+ /**
+ * Sign-out action applies to all sessions across the entire system.
+ */
+ GLOBAL,
+
+ /**
+ * Sign-out action applies only to the current session.
+ */
+ LOCAL,
+
+ /**
+ * Sign-out action applies to other sessions, excluding the current session.
+ */
+ OTHERS
+}
\ No newline at end of file
diff --git a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/Utils.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/Utils.kt
similarity index 92%
rename from GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/Utils.kt
rename to Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/Utils.kt
index 803e19979..bb3f4ce30 100644
--- a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/Utils.kt
+++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/Utils.kt
@@ -1,8 +1,8 @@
-package io.github.jan.supabase.gotrue
+package io.github.jan.supabase.auth
import io.github.jan.supabase.SupabaseClient
import io.github.jan.supabase.annotations.SupabaseInternal
-import io.github.jan.supabase.gotrue.user.UserSession
+import io.github.jan.supabase.auth.user.UserSession
import io.github.jan.supabase.logging.d
import io.ktor.client.request.HttpRequestBuilder
import kotlinx.coroutines.launch
diff --git a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/admin/AdminApi.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/admin/AdminApi.kt
similarity index 95%
rename from GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/admin/AdminApi.kt
rename to Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/admin/AdminApi.kt
index 6b7d946e6..0eb69881b 100644
--- a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/admin/AdminApi.kt
+++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/admin/AdminApi.kt
@@ -1,11 +1,11 @@
-package io.github.jan.supabase.gotrue.admin
+package io.github.jan.supabase.auth.admin
import io.github.jan.supabase.annotations.SupabaseInternal
-import io.github.jan.supabase.gotrue.Auth
-import io.github.jan.supabase.gotrue.AuthImpl
-import io.github.jan.supabase.gotrue.SignOutScope
-import io.github.jan.supabase.gotrue.user.UserInfo
-import io.github.jan.supabase.gotrue.user.UserMfaFactor
+import io.github.jan.supabase.auth.Auth
+import io.github.jan.supabase.auth.AuthImpl
+import io.github.jan.supabase.auth.SignOutScope
+import io.github.jan.supabase.auth.user.UserInfo
+import io.github.jan.supabase.auth.user.UserMfaFactor
import io.github.jan.supabase.putJsonObject
import io.github.jan.supabase.safeBody
import io.github.jan.supabase.supabaseJson
diff --git a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/admin/AdminUserBuilder.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/admin/AdminUserBuilder.kt
similarity index 98%
rename from GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/admin/AdminUserBuilder.kt
rename to Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/admin/AdminUserBuilder.kt
index df16e5818..d7e1b9812 100644
--- a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/admin/AdminUserBuilder.kt
+++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/admin/AdminUserBuilder.kt
@@ -1,4 +1,4 @@
-package io.github.jan.supabase.gotrue.admin
+package io.github.jan.supabase.auth.admin
import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializable
diff --git a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/admin/AdminUserUpdateBuilder.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/admin/AdminUserUpdateBuilder.kt
similarity index 97%
rename from GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/admin/AdminUserUpdateBuilder.kt
rename to Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/admin/AdminUserUpdateBuilder.kt
index b227c1950..e15152ef5 100644
--- a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/admin/AdminUserUpdateBuilder.kt
+++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/admin/AdminUserUpdateBuilder.kt
@@ -1,4 +1,4 @@
-package io.github.jan.supabase.gotrue.admin
+package io.github.jan.supabase.auth.admin
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
diff --git a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/admin/LinkType.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/admin/LinkType.kt
similarity index 98%
rename from GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/admin/LinkType.kt
rename to Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/admin/LinkType.kt
index d37fda7df..843c67e54 100644
--- a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/admin/LinkType.kt
+++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/admin/LinkType.kt
@@ -1,4 +1,4 @@
-package io.github.jan.supabase.gotrue.admin
+package io.github.jan.supabase.auth.admin
import io.github.jan.supabase.annotations.SupabaseInternal
import kotlinx.serialization.SerialName
diff --git a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/exception/AuthErrorCode.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/exception/AuthErrorCode.kt
similarity index 97%
rename from GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/exception/AuthErrorCode.kt
rename to Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/exception/AuthErrorCode.kt
index 53d5ddfb3..7d3097684 100644
--- a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/exception/AuthErrorCode.kt
+++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/exception/AuthErrorCode.kt
@@ -1,4 +1,5 @@
-package io.github.jan.supabase.gotrue.exception
+@file:Suppress("UndocumentedPublicProperty")
+package io.github.jan.supabase.auth.exception
/**
* Enum class for error codes returned by the Auth API.
diff --git a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/exception/AuthRestException.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/exception/AuthRestException.kt
similarity index 94%
rename from GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/exception/AuthRestException.kt
rename to Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/exception/AuthRestException.kt
index 6dbd18a96..f5c123a10 100644
--- a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/exception/AuthRestException.kt
+++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/exception/AuthRestException.kt
@@ -1,4 +1,4 @@
-package io.github.jan.supabase.gotrue.exception
+package io.github.jan.supabase.auth.exception
import io.github.jan.supabase.exceptions.RestException
diff --git a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/exception/AuthSessionMissingException.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/exception/AuthSessionMissingException.kt
similarity index 88%
rename from GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/exception/AuthSessionMissingException.kt
rename to Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/exception/AuthSessionMissingException.kt
index 74379c06a..f5a180f10 100644
--- a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/exception/AuthSessionMissingException.kt
+++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/exception/AuthSessionMissingException.kt
@@ -1,4 +1,4 @@
-package io.github.jan.supabase.gotrue.exception
+package io.github.jan.supabase.auth.exception
/**
* Exception thrown when a session is not found.
diff --git a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/exception/AuthWeakPasswordException.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/exception/AuthWeakPasswordException.kt
similarity index 90%
rename from GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/exception/AuthWeakPasswordException.kt
rename to Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/exception/AuthWeakPasswordException.kt
index 9b5b2e4c8..d9d1a6dc3 100644
--- a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/exception/AuthWeakPasswordException.kt
+++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/exception/AuthWeakPasswordException.kt
@@ -1,4 +1,4 @@
-package io.github.jan.supabase.gotrue.exception
+package io.github.jan.supabase.auth.exception
/**
* Exception thrown on sign-up if the password is too weak
diff --git a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/mfa/AuthenticatorAssuranceLevel.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/mfa/AuthenticatorAssuranceLevel.kt
similarity index 96%
rename from GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/mfa/AuthenticatorAssuranceLevel.kt
rename to Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/mfa/AuthenticatorAssuranceLevel.kt
index d223a8cea..2dff23a4c 100644
--- a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/mfa/AuthenticatorAssuranceLevel.kt
+++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/mfa/AuthenticatorAssuranceLevel.kt
@@ -1,4 +1,4 @@
-package io.github.jan.supabase.gotrue.mfa
+package io.github.jan.supabase.auth.mfa
/**
* The assurance level of a session
diff --git a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/mfa/FactorType.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/mfa/FactorType.kt
similarity index 98%
rename from GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/mfa/FactorType.kt
rename to Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/mfa/FactorType.kt
index 93bcd11b3..c5b7072b6 100644
--- a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/mfa/FactorType.kt
+++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/mfa/FactorType.kt
@@ -1,4 +1,4 @@
-package io.github.jan.supabase.gotrue.mfa
+package io.github.jan.supabase.auth.mfa
import io.github.jan.supabase.annotations.SupabaseInternal
import io.github.jan.supabase.supabaseJson
diff --git a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/mfa/MfaApi.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/mfa/MfaApi.kt
similarity index 77%
rename from GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/mfa/MfaApi.kt
rename to Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/mfa/MfaApi.kt
index 22b2fc9aa..b7ba3ab16 100644
--- a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/mfa/MfaApi.kt
+++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/mfa/MfaApi.kt
@@ -1,11 +1,11 @@
-package io.github.jan.supabase.gotrue.mfa
-
-import io.github.jan.supabase.gotrue.Auth
-import io.github.jan.supabase.gotrue.AuthImpl
-import io.github.jan.supabase.gotrue.SessionStatus
-import io.github.jan.supabase.gotrue.providers.builtin.Phone
-import io.github.jan.supabase.gotrue.user.UserMfaFactor
-import io.github.jan.supabase.gotrue.user.UserSession
+package io.github.jan.supabase.auth.mfa
+
+import io.github.jan.supabase.auth.Auth
+import io.github.jan.supabase.auth.AuthImpl
+import io.github.jan.supabase.auth.SessionStatus
+import io.github.jan.supabase.auth.providers.builtin.Phone
+import io.github.jan.supabase.auth.user.UserMfaFactor
+import io.github.jan.supabase.auth.user.UserSession
import io.github.jan.supabase.putJsonObject
import io.github.jan.supabase.safeBody
import io.ktor.client.call.body
@@ -37,43 +37,12 @@ sealed interface MfaApi {
*/
val statusFlow: Flow
- /**
- * Checks whether the current session was authenticated with MFA or the user has a verified MFA factor. Note that if the user has verified a factor on another device and the user hasn't been updated on this device, this will return false.
- * You can use [Auth.retrieveUserForCurrentSession] to update the user and this property will update.
- */
- @Deprecated("Use statusFlow instead", ReplaceWith("statusFlow"))
- val isMfaEnabledFlow: Flow
-
- /**
- * Checks whether the user has a verified MFA factor. Note that if the user has verified a factor on another device and the user hasn't been updated on this device, this will return false.
- * You can use [Auth.retrieveUserForCurrentSession] to update the user and this property will update.
- */
- @Deprecated("Use status.enabled instead", ReplaceWith("status.enabled"))
- val isMfaEnabled: Boolean
- get() {
- val mfaLevel = getAuthenticatorAssuranceLevel()
- return mfaLevel.next == AuthenticatorAssuranceLevel.AAL2
- }
-
/**
* Returns all verified factors from the current user session. If the user has no verified factors or there is no session, an empty list is returned.
* To fetch up-to-date factors, use [retrieveFactorsForCurrentUser].
*/
val verifiedFactors: List
- /**
- * Checks whether the current session is authenticated with MFA
- */
- @Deprecated("Use status.active instead", ReplaceWith("status.active"))
- val loggedInUsingMfa: Boolean
- get() = getAuthenticatorAssuranceLevel().current == AuthenticatorAssuranceLevel.AAL2
-
- /**
- * Checks whether the current session is authenticated with MFA
- */
- @Deprecated("Use statusFlow instead", ReplaceWith("statusFlow"))
- val loggedInUsingMfaFlow: Flow
-
/**
* @param factorType The type of MFA factor to enroll. Currently only supports TOTP.
* @param friendlyName Human-readable name assigned to a device
@@ -156,10 +125,6 @@ internal class MfaApiImpl(
}
}
- @Deprecated("Use statusFlow instead", replaceWith = ReplaceWith("statusFlow"))
- override val isMfaEnabledFlow: Flow = statusFlow.map { it.enabled }
- @Deprecated("Use statusFlow instead", replaceWith = ReplaceWith("statusFlow"))
- override val loggedInUsingMfaFlow: Flow = statusFlow.map { it.active }
override val verifiedFactors: List
get() = auth.currentUserOrNull()?.factors?.filter(UserMfaFactor::isVerified) ?: emptyList()
diff --git a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/mfa/MfaChallenge.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/mfa/MfaChallenge.kt
similarity index 93%
rename from GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/mfa/MfaChallenge.kt
rename to Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/mfa/MfaChallenge.kt
index e72739ada..15128f8f1 100644
--- a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/mfa/MfaChallenge.kt
+++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/mfa/MfaChallenge.kt
@@ -1,4 +1,4 @@
-package io.github.jan.supabase.gotrue.mfa
+package io.github.jan.supabase.auth.mfa
import kotlinx.datetime.Instant
import kotlinx.serialization.SerialName
diff --git a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/mfa/MfaFactor.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/mfa/MfaFactor.kt
similarity index 86%
rename from GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/mfa/MfaFactor.kt
rename to Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/mfa/MfaFactor.kt
index f9c5c52cb..d1d66998e 100644
--- a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/mfa/MfaFactor.kt
+++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/mfa/MfaFactor.kt
@@ -1,4 +1,4 @@
-package io.github.jan.supabase.gotrue.mfa
+package io.github.jan.supabase.auth.mfa
/**
* Represents an enrolled MFA Factor
diff --git a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/mfa/MfaStatus.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/mfa/MfaStatus.kt
similarity index 89%
rename from GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/mfa/MfaStatus.kt
rename to Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/mfa/MfaStatus.kt
index 0b89a6df2..f92bfcdd2 100644
--- a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/mfa/MfaStatus.kt
+++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/mfa/MfaStatus.kt
@@ -1,4 +1,4 @@
-package io.github.jan.supabase.gotrue.mfa
+package io.github.jan.supabase.auth.mfa
/**
* Represents the MFA status of a user.
diff --git a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/providers/AuthProvider.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/providers/AuthProvider.kt
similarity index 91%
rename from GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/providers/AuthProvider.kt
rename to Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/providers/AuthProvider.kt
index 881a7641b..f53468584 100644
--- a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/providers/AuthProvider.kt
+++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/providers/AuthProvider.kt
@@ -1,7 +1,7 @@
-package io.github.jan.supabase.gotrue.providers
+package io.github.jan.supabase.auth.providers
import io.github.jan.supabase.SupabaseClient
-import io.github.jan.supabase.gotrue.user.UserSession
+import io.github.jan.supabase.auth.user.UserSession
/**
* An authentication provider
diff --git a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/providers/ExternalAuthConfig.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/providers/ExternalAuthConfig.kt
similarity index 87%
rename from GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/providers/ExternalAuthConfig.kt
rename to Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/providers/ExternalAuthConfig.kt
index 7fffe5f25..d25fb73e6 100644
--- a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/providers/ExternalAuthConfig.kt
+++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/providers/ExternalAuthConfig.kt
@@ -1,6 +1,6 @@
-package io.github.jan.supabase.gotrue.providers
+package io.github.jan.supabase.auth.providers
-import io.github.jan.supabase.gotrue.Auth
+import io.github.jan.supabase.auth.Auth
/**
* Configuration for external authentication providers like Google, Twitter, etc.
diff --git a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/providers/IDTokenProvider.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/providers/IDTokenProvider.kt
similarity index 94%
rename from GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/providers/IDTokenProvider.kt
rename to Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/providers/IDTokenProvider.kt
index a15eebb5c..6dd56a252 100644
--- a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/providers/IDTokenProvider.kt
+++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/providers/IDTokenProvider.kt
@@ -1,4 +1,4 @@
-package io.github.jan.supabase.gotrue.providers
+package io.github.jan.supabase.auth.providers
import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializable
diff --git a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/providers/OAuthProvider.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/providers/OAuthProvider.kt
similarity index 86%
rename from GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/providers/OAuthProvider.kt
rename to Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/providers/OAuthProvider.kt
index 1b8a1f84f..f50d0f7fe 100644
--- a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/providers/OAuthProvider.kt
+++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/providers/OAuthProvider.kt
@@ -1,9 +1,9 @@
-package io.github.jan.supabase.gotrue.providers
+package io.github.jan.supabase.auth.providers
import io.github.jan.supabase.SupabaseClient
-import io.github.jan.supabase.gotrue.auth
-import io.github.jan.supabase.gotrue.startExternalAuth
-import io.github.jan.supabase.gotrue.user.UserSession
+import io.github.jan.supabase.auth.auth
+import io.github.jan.supabase.auth.startExternalAuth
+import io.github.jan.supabase.auth.user.UserSession
/**
* Represents an OAuth provider.
diff --git a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/providers/Providers.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/providers/Providers.kt
similarity index 97%
rename from GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/providers/Providers.kt
rename to Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/providers/Providers.kt
index 54f19f1bf..698532e31 100644
--- a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/providers/Providers.kt
+++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/providers/Providers.kt
@@ -1,5 +1,5 @@
@file:Suppress("UndocumentedPublicClass")
-package io.github.jan.supabase.gotrue.providers
+package io.github.jan.supabase.auth.providers
data object Google : IDTokenProvider() {
diff --git a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/providers/builtin/CaptchaTokenSerializer.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/providers/builtin/CaptchaTokenSerializer.kt
similarity index 95%
rename from GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/providers/builtin/CaptchaTokenSerializer.kt
rename to Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/providers/builtin/CaptchaTokenSerializer.kt
index b6eca4b6e..d5e6014dd 100644
--- a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/providers/builtin/CaptchaTokenSerializer.kt
+++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/providers/builtin/CaptchaTokenSerializer.kt
@@ -1,4 +1,4 @@
-package io.github.jan.supabase.gotrue.providers.builtin
+package io.github.jan.supabase.auth.providers.builtin
import io.github.jan.supabase.annotations.SupabaseInternal
import kotlinx.serialization.KSerializer
diff --git a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/providers/builtin/DefaultAuthProvider.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/providers/builtin/DefaultAuthProvider.kt
similarity index 86%
rename from GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/providers/builtin/DefaultAuthProvider.kt
rename to Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/providers/builtin/DefaultAuthProvider.kt
index 7dff8128d..b328cb296 100644
--- a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/providers/builtin/DefaultAuthProvider.kt
+++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/providers/builtin/DefaultAuthProvider.kt
@@ -1,17 +1,17 @@
-package io.github.jan.supabase.gotrue.providers.builtin
+package io.github.jan.supabase.auth.providers.builtin
import io.github.jan.supabase.SupabaseClient
import io.github.jan.supabase.annotations.SupabaseExperimental
import io.github.jan.supabase.annotations.SupabaseInternal
-import io.github.jan.supabase.gotrue.AuthImpl
-import io.github.jan.supabase.gotrue.FlowType
-import io.github.jan.supabase.gotrue.auth
-import io.github.jan.supabase.gotrue.generateCodeChallenge
-import io.github.jan.supabase.gotrue.generateCodeVerifier
-import io.github.jan.supabase.gotrue.providers.AuthProvider
-import io.github.jan.supabase.gotrue.putCodeChallenge
-import io.github.jan.supabase.gotrue.redirectTo
-import io.github.jan.supabase.gotrue.user.UserSession
+import io.github.jan.supabase.auth.AuthImpl
+import io.github.jan.supabase.auth.FlowType
+import io.github.jan.supabase.auth.auth
+import io.github.jan.supabase.auth.generateCodeChallenge
+import io.github.jan.supabase.auth.generateCodeVerifier
+import io.github.jan.supabase.auth.providers.AuthProvider
+import io.github.jan.supabase.auth.putCodeChallenge
+import io.github.jan.supabase.auth.redirectTo
+import io.github.jan.supabase.auth.user.UserSession
import io.github.jan.supabase.putJsonObject
import io.github.jan.supabase.supabaseJson
import io.ktor.client.call.body
diff --git a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/providers/builtin/Email.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/providers/builtin/Email.kt
similarity index 92%
rename from GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/providers/builtin/Email.kt
rename to Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/providers/builtin/Email.kt
index 8d217c896..5fb777aab 100644
--- a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/providers/builtin/Email.kt
+++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/providers/builtin/Email.kt
@@ -1,7 +1,7 @@
-package io.github.jan.supabase.gotrue.providers.builtin
+package io.github.jan.supabase.auth.providers.builtin
+import io.github.jan.supabase.auth.user.UserInfo
import io.github.jan.supabase.exceptions.SupabaseEncodingException
-import io.github.jan.supabase.gotrue.user.UserInfo
import io.github.jan.supabase.supabaseJson
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.MissingFieldException
diff --git a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/providers/builtin/IDToken.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/providers/builtin/IDToken.kt
similarity index 86%
rename from GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/providers/builtin/IDToken.kt
rename to Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/providers/builtin/IDToken.kt
index 655158cd6..877fd5a4d 100644
--- a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/providers/builtin/IDToken.kt
+++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/providers/builtin/IDToken.kt
@@ -1,12 +1,12 @@
-package io.github.jan.supabase.gotrue.providers.builtin
+package io.github.jan.supabase.auth.providers.builtin
+import io.github.jan.supabase.auth.providers.Apple
+import io.github.jan.supabase.auth.providers.Azure
+import io.github.jan.supabase.auth.providers.Facebook
+import io.github.jan.supabase.auth.providers.Google
+import io.github.jan.supabase.auth.providers.IDTokenProvider
+import io.github.jan.supabase.auth.user.UserInfo
import io.github.jan.supabase.exceptions.SupabaseEncodingException
-import io.github.jan.supabase.gotrue.providers.Apple
-import io.github.jan.supabase.gotrue.providers.Azure
-import io.github.jan.supabase.gotrue.providers.Facebook
-import io.github.jan.supabase.gotrue.providers.Google
-import io.github.jan.supabase.gotrue.providers.IDTokenProvider
-import io.github.jan.supabase.gotrue.user.UserInfo
import io.github.jan.supabase.supabaseJson
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.MissingFieldException
diff --git a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/providers/builtin/OTP.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/providers/builtin/OTP.kt
similarity index 86%
rename from GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/providers/builtin/OTP.kt
rename to Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/providers/builtin/OTP.kt
index eb434953e..3875f0d62 100644
--- a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/providers/builtin/OTP.kt
+++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/providers/builtin/OTP.kt
@@ -1,17 +1,16 @@
-package io.github.jan.supabase.gotrue.providers.builtin
+package io.github.jan.supabase.auth.providers.builtin
import io.github.jan.supabase.SupabaseClient
import io.github.jan.supabase.SupabaseSerializer
+import io.github.jan.supabase.auth.AuthImpl
+import io.github.jan.supabase.auth.FlowType
+import io.github.jan.supabase.auth.auth
+import io.github.jan.supabase.auth.generateCodeChallenge
+import io.github.jan.supabase.auth.generateCodeVerifier
+import io.github.jan.supabase.auth.providers.AuthProvider
+import io.github.jan.supabase.auth.putCaptchaToken
+import io.github.jan.supabase.auth.user.UserSession
import io.github.jan.supabase.encodeToJsonElement
-import io.github.jan.supabase.gotrue.Auth
-import io.github.jan.supabase.gotrue.AuthImpl
-import io.github.jan.supabase.gotrue.FlowType
-import io.github.jan.supabase.gotrue.auth
-import io.github.jan.supabase.gotrue.generateCodeChallenge
-import io.github.jan.supabase.gotrue.generateCodeVerifier
-import io.github.jan.supabase.gotrue.providers.AuthProvider
-import io.github.jan.supabase.gotrue.putCaptchaToken
-import io.github.jan.supabase.gotrue.user.UserSession
import io.github.jan.supabase.putJsonObject
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.buildJsonObject
diff --git a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/providers/builtin/Phone.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/providers/builtin/Phone.kt
similarity index 96%
rename from GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/providers/builtin/Phone.kt
rename to Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/providers/builtin/Phone.kt
index e8e6f798a..0718dacbe 100644
--- a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/providers/builtin/Phone.kt
+++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/providers/builtin/Phone.kt
@@ -1,7 +1,7 @@
-package io.github.jan.supabase.gotrue.providers.builtin
+package io.github.jan.supabase.auth.providers.builtin
+import io.github.jan.supabase.auth.user.UserInfo
import io.github.jan.supabase.exceptions.SupabaseEncodingException
-import io.github.jan.supabase.gotrue.user.UserInfo
import io.github.jan.supabase.supabaseJson
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.KSerializer
diff --git a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/providers/builtin/SSO.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/providers/builtin/SSO.kt
similarity index 87%
rename from GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/providers/builtin/SSO.kt
rename to Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/providers/builtin/SSO.kt
index f4c4361ad..783e75ce1 100644
--- a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/providers/builtin/SSO.kt
+++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/providers/builtin/SSO.kt
@@ -1,10 +1,10 @@
-package io.github.jan.supabase.gotrue.providers.builtin
+package io.github.jan.supabase.auth.providers.builtin
import io.github.jan.supabase.SupabaseClient
-import io.github.jan.supabase.gotrue.auth
-import io.github.jan.supabase.gotrue.providers.AuthProvider
-import io.github.jan.supabase.gotrue.startExternalAuth
-import io.github.jan.supabase.gotrue.user.UserSession
+import io.github.jan.supabase.auth.auth
+import io.github.jan.supabase.auth.providers.AuthProvider
+import io.github.jan.supabase.auth.startExternalAuth
+import io.github.jan.supabase.auth.user.UserSession
import kotlinx.serialization.Serializable
/**
diff --git a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/user/Identity.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/user/Identity.kt
similarity index 94%
rename from GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/user/Identity.kt
rename to Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/user/Identity.kt
index d6f1d71ba..46219e370 100644
--- a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/user/Identity.kt
+++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/user/Identity.kt
@@ -1,5 +1,5 @@
@file:Suppress("UndocumentedPublicClass", "UndocumentedPublicFunction", "UndocumentedPublicProperty")
-package io.github.jan.supabase.gotrue.user
+package io.github.jan.supabase.auth.user
import kotlinx.serialization.SerialName
diff --git a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/user/UserInfo.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/user/UserInfo.kt
similarity index 98%
rename from GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/user/UserInfo.kt
rename to Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/user/UserInfo.kt
index dca8f948d..8bb59ed7a 100644
--- a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/user/UserInfo.kt
+++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/user/UserInfo.kt
@@ -1,5 +1,5 @@
@file:Suppress("UndocumentedPublicClass", "UndocumentedPublicFunction", "UndocumentedPublicProperty")
-package io.github.jan.supabase.gotrue.user
+package io.github.jan.supabase.auth.user
import kotlinx.datetime.Instant
import kotlinx.serialization.SerialName
diff --git a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/user/UserSession.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/user/UserSession.kt
similarity index 95%
rename from GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/user/UserSession.kt
rename to Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/user/UserSession.kt
index 33fb742fb..12ef9f9e2 100644
--- a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/user/UserSession.kt
+++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/user/UserSession.kt
@@ -1,5 +1,5 @@
@file:Suppress("UndocumentedPublicClass", "UndocumentedPublicFunction", "UndocumentedPublicProperty")
-package io.github.jan.supabase.gotrue.user
+package io.github.jan.supabase.auth.user
import kotlinx.datetime.Clock
import kotlinx.datetime.Instant
diff --git a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/user/UserUpdateBuilder.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/user/UserUpdateBuilder.kt
similarity index 94%
rename from GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/user/UserUpdateBuilder.kt
rename to Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/user/UserUpdateBuilder.kt
index 07e484ac2..a4fef6c71 100644
--- a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/user/UserUpdateBuilder.kt
+++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/user/UserUpdateBuilder.kt
@@ -1,8 +1,8 @@
-package io.github.jan.supabase.gotrue.user
+package io.github.jan.supabase.auth.user
import io.github.jan.supabase.SupabaseSerializer
+import io.github.jan.supabase.auth.Auth
import io.github.jan.supabase.encodeToJsonElement
-import io.github.jan.supabase.gotrue.Auth
import io.github.jan.supabase.serializer.KotlinXSerializer
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
diff --git a/GoTrue/src/commonTest/kotlin/AccessTokenTest.kt b/Auth/src/commonTest/kotlin/AccessTokenTest.kt
similarity index 91%
rename from GoTrue/src/commonTest/kotlin/AccessTokenTest.kt
rename to Auth/src/commonTest/kotlin/AccessTokenTest.kt
index 2c962fa7f..ad6647495 100644
--- a/GoTrue/src/commonTest/kotlin/AccessTokenTest.kt
+++ b/Auth/src/commonTest/kotlin/AccessTokenTest.kt
@@ -1,7 +1,7 @@
-import io.github.jan.supabase.gotrue.Auth
-import io.github.jan.supabase.gotrue.auth
-import io.github.jan.supabase.gotrue.minimalSettings
-import io.github.jan.supabase.gotrue.resolveAccessToken
+import io.github.jan.supabase.auth.Auth
+import io.github.jan.supabase.auth.auth
+import io.github.jan.supabase.auth.minimalSettings
+import io.github.jan.supabase.auth.resolveAccessToken
import io.github.jan.supabase.testing.createMockedSupabaseClient
import kotlinx.coroutines.test.runTest
import kotlin.test.Test
diff --git a/GoTrue/src/commonTest/kotlin/AdminApiTest.kt b/Auth/src/commonTest/kotlin/AdminApiTest.kt
similarity index 97%
rename from GoTrue/src/commonTest/kotlin/AdminApiTest.kt
rename to Auth/src/commonTest/kotlin/AdminApiTest.kt
index 21ecfbf19..1e899e027 100644
--- a/GoTrue/src/commonTest/kotlin/AdminApiTest.kt
+++ b/Auth/src/commonTest/kotlin/AdminApiTest.kt
@@ -1,12 +1,12 @@
import io.github.jan.supabase.SupabaseClientBuilder
-import io.github.jan.supabase.gotrue.Auth
-import io.github.jan.supabase.gotrue.SignOutScope
-import io.github.jan.supabase.gotrue.admin.LinkType
-import io.github.jan.supabase.gotrue.admin.generateLinkFor
-import io.github.jan.supabase.gotrue.auth
-import io.github.jan.supabase.gotrue.minimalSettings
-import io.github.jan.supabase.gotrue.user.UserInfo
-import io.github.jan.supabase.gotrue.user.UserMfaFactor
+import io.github.jan.supabase.auth.Auth
+import io.github.jan.supabase.auth.SignOutScope
+import io.github.jan.supabase.auth.admin.LinkType
+import io.github.jan.supabase.auth.admin.generateLinkFor
+import io.github.jan.supabase.auth.auth
+import io.github.jan.supabase.auth.minimalSettings
+import io.github.jan.supabase.auth.user.UserInfo
+import io.github.jan.supabase.auth.user.UserMfaFactor
import io.github.jan.supabase.testing.assertMethodIs
import io.github.jan.supabase.testing.assertPathIs
import io.github.jan.supabase.testing.createMockedSupabaseClient
diff --git a/GoTrue/src/commonTest/kotlin/AuthApiTest.kt b/Auth/src/commonTest/kotlin/AuthApiTest.kt
similarity index 97%
rename from GoTrue/src/commonTest/kotlin/AuthApiTest.kt
rename to Auth/src/commonTest/kotlin/AuthApiTest.kt
index 5f6c6ea0b..364ffdb31 100644
--- a/GoTrue/src/commonTest/kotlin/AuthApiTest.kt
+++ b/Auth/src/commonTest/kotlin/AuthApiTest.kt
@@ -1,18 +1,18 @@
import io.github.jan.supabase.SupabaseClientBuilder
-import io.github.jan.supabase.gotrue.Auth
-import io.github.jan.supabase.gotrue.AuthConfig
-import io.github.jan.supabase.gotrue.FlowType
-import io.github.jan.supabase.gotrue.OtpType
-import io.github.jan.supabase.gotrue.PKCEConstants
-import io.github.jan.supabase.gotrue.SessionSource
-import io.github.jan.supabase.gotrue.SignOutScope
-import io.github.jan.supabase.gotrue.auth
-import io.github.jan.supabase.gotrue.minimalSettings
-import io.github.jan.supabase.gotrue.providers.Google
-import io.github.jan.supabase.gotrue.providers.builtin.Email
-import io.github.jan.supabase.gotrue.providers.builtin.IDToken
-import io.github.jan.supabase.gotrue.providers.builtin.OTP
-import io.github.jan.supabase.gotrue.providers.builtin.Phone
+import io.github.jan.supabase.auth.Auth
+import io.github.jan.supabase.auth.AuthConfig
+import io.github.jan.supabase.auth.FlowType
+import io.github.jan.supabase.auth.OtpType
+import io.github.jan.supabase.auth.PKCEConstants
+import io.github.jan.supabase.auth.SessionSource
+import io.github.jan.supabase.auth.SignOutScope
+import io.github.jan.supabase.auth.auth
+import io.github.jan.supabase.auth.minimalSettings
+import io.github.jan.supabase.auth.providers.Google
+import io.github.jan.supabase.auth.providers.builtin.Email
+import io.github.jan.supabase.auth.providers.builtin.IDToken
+import io.github.jan.supabase.auth.providers.builtin.OTP
+import io.github.jan.supabase.auth.providers.builtin.Phone
import io.github.jan.supabase.testing.assertMethodIs
import io.github.jan.supabase.testing.assertPathIs
import io.github.jan.supabase.testing.createMockedSupabaseClient
diff --git a/GoTrue/src/commonTest/kotlin/AuthRestExceptionTest.kt b/Auth/src/commonTest/kotlin/AuthRestExceptionTest.kt
similarity index 91%
rename from GoTrue/src/commonTest/kotlin/AuthRestExceptionTest.kt
rename to Auth/src/commonTest/kotlin/AuthRestExceptionTest.kt
index a9fff77a6..39db4412b 100644
--- a/GoTrue/src/commonTest/kotlin/AuthRestExceptionTest.kt
+++ b/Auth/src/commonTest/kotlin/AuthRestExceptionTest.kt
@@ -1,11 +1,11 @@
import io.github.jan.supabase.SupabaseClientBuilder
+import io.github.jan.supabase.auth.Auth
+import io.github.jan.supabase.auth.auth
+import io.github.jan.supabase.auth.exception.AuthRestException
+import io.github.jan.supabase.auth.exception.AuthWeakPasswordException
+import io.github.jan.supabase.auth.minimalSettings
+import io.github.jan.supabase.auth.providers.builtin.Email
import io.github.jan.supabase.exceptions.BadRequestRestException
-import io.github.jan.supabase.gotrue.Auth
-import io.github.jan.supabase.gotrue.auth
-import io.github.jan.supabase.gotrue.exception.AuthRestException
-import io.github.jan.supabase.gotrue.exception.AuthWeakPasswordException
-import io.github.jan.supabase.gotrue.minimalSettings
-import io.github.jan.supabase.gotrue.providers.builtin.Email
import io.github.jan.supabase.testing.createMockedSupabaseClient
import io.github.jan.supabase.testing.respondJson
import io.ktor.http.HttpStatusCode
diff --git a/GoTrue/src/commonTest/kotlin/AuthTest.kt b/Auth/src/commonTest/kotlin/AuthTest.kt
similarity index 94%
rename from GoTrue/src/commonTest/kotlin/AuthTest.kt
rename to Auth/src/commonTest/kotlin/AuthTest.kt
index 584149532..ed071de96 100644
--- a/GoTrue/src/commonTest/kotlin/AuthTest.kt
+++ b/Auth/src/commonTest/kotlin/AuthTest.kt
@@ -1,13 +1,13 @@
import io.github.jan.supabase.SupabaseClientBuilder
-import io.github.jan.supabase.gotrue.Auth
-import io.github.jan.supabase.gotrue.MemorySessionManager
-import io.github.jan.supabase.gotrue.SessionStatus
-import io.github.jan.supabase.gotrue.auth
-import io.github.jan.supabase.gotrue.minimalSettings
-import io.github.jan.supabase.gotrue.providers.Github
-import io.github.jan.supabase.gotrue.user.Identity
-import io.github.jan.supabase.gotrue.user.UserInfo
-import io.github.jan.supabase.gotrue.user.UserSession
+import io.github.jan.supabase.auth.Auth
+import io.github.jan.supabase.auth.MemorySessionManager
+import io.github.jan.supabase.auth.SessionStatus
+import io.github.jan.supabase.auth.auth
+import io.github.jan.supabase.auth.minimalSettings
+import io.github.jan.supabase.auth.providers.Github
+import io.github.jan.supabase.auth.user.Identity
+import io.github.jan.supabase.auth.user.UserInfo
+import io.github.jan.supabase.auth.user.UserSession
import io.github.jan.supabase.testing.createMockedSupabaseClient
import io.github.jan.supabase.testing.pathAfterVersion
import io.github.jan.supabase.testing.respondJson
diff --git a/GoTrue/src/commonTest/kotlin/AuthTestUtils.kt b/Auth/src/commonTest/kotlin/AuthTestUtils.kt
similarity index 62%
rename from GoTrue/src/commonTest/kotlin/AuthTestUtils.kt
rename to Auth/src/commonTest/kotlin/AuthTestUtils.kt
index b271e5737..48439e432 100644
--- a/GoTrue/src/commonTest/kotlin/AuthTestUtils.kt
+++ b/Auth/src/commonTest/kotlin/AuthTestUtils.kt
@@ -1,5 +1,5 @@
-import io.github.jan.supabase.gotrue.Auth
-import io.github.jan.supabase.gotrue.SessionStatus
+import io.github.jan.supabase.auth.Auth
+import io.github.jan.supabase.auth.SessionStatus
fun Auth.sessionSource() = (sessionStatus.value as SessionStatus.Authenticated).source
diff --git a/GoTrue/src/commonTest/kotlin/CodeVerifierCacheTest.kt b/Auth/src/commonTest/kotlin/CodeVerifierCacheTest.kt
similarity index 92%
rename from GoTrue/src/commonTest/kotlin/CodeVerifierCacheTest.kt
rename to Auth/src/commonTest/kotlin/CodeVerifierCacheTest.kt
index d22a34240..bc5fe97e9 100644
--- a/GoTrue/src/commonTest/kotlin/CodeVerifierCacheTest.kt
+++ b/Auth/src/commonTest/kotlin/CodeVerifierCacheTest.kt
@@ -1,4 +1,4 @@
-import io.github.jan.supabase.gotrue.MemoryCodeVerifierCache
+import io.github.jan.supabase.auth.MemoryCodeVerifierCache
import kotlinx.coroutines.test.runTest
import kotlin.test.Test
import kotlin.test.assertEquals
diff --git a/GoTrue/src/commonTest/kotlin/MemorySessionManagerTest.kt b/Auth/src/commonTest/kotlin/MemorySessionManagerTest.kt
similarity index 94%
rename from GoTrue/src/commonTest/kotlin/MemorySessionManagerTest.kt
rename to Auth/src/commonTest/kotlin/MemorySessionManagerTest.kt
index ee213fbbf..316c16a72 100644
--- a/GoTrue/src/commonTest/kotlin/MemorySessionManagerTest.kt
+++ b/Auth/src/commonTest/kotlin/MemorySessionManagerTest.kt
@@ -1,4 +1,4 @@
-import io.github.jan.supabase.gotrue.MemorySessionManager
+import io.github.jan.supabase.auth.MemorySessionManager
import kotlinx.coroutines.test.runTest
import kotlin.test.Test
import kotlin.test.assertEquals
diff --git a/GoTrue/src/commonTest/kotlin/MfaApiTest.kt b/Auth/src/commonTest/kotlin/MfaApiTest.kt
similarity index 95%
rename from GoTrue/src/commonTest/kotlin/MfaApiTest.kt
rename to Auth/src/commonTest/kotlin/MfaApiTest.kt
index df08a83d4..67571e6af 100644
--- a/GoTrue/src/commonTest/kotlin/MfaApiTest.kt
+++ b/Auth/src/commonTest/kotlin/MfaApiTest.kt
@@ -1,15 +1,15 @@
import app.cash.turbine.test
import io.github.jan.supabase.SupabaseClientBuilder
-import io.github.jan.supabase.gotrue.Auth
-import io.github.jan.supabase.gotrue.auth
-import io.github.jan.supabase.gotrue.mfa.AuthenticatorAssuranceLevel
-import io.github.jan.supabase.gotrue.mfa.FactorType
-import io.github.jan.supabase.gotrue.mfa.MfaStatus
-import io.github.jan.supabase.gotrue.minimalSettings
-import io.github.jan.supabase.gotrue.providers.builtin.Phone
-import io.github.jan.supabase.gotrue.user.UserInfo
-import io.github.jan.supabase.gotrue.user.UserMfaFactor
-import io.github.jan.supabase.gotrue.user.UserSession
+import io.github.jan.supabase.auth.Auth
+import io.github.jan.supabase.auth.auth
+import io.github.jan.supabase.auth.mfa.AuthenticatorAssuranceLevel
+import io.github.jan.supabase.auth.mfa.FactorType
+import io.github.jan.supabase.auth.mfa.MfaStatus
+import io.github.jan.supabase.auth.minimalSettings
+import io.github.jan.supabase.auth.providers.builtin.Phone
+import io.github.jan.supabase.auth.user.UserInfo
+import io.github.jan.supabase.auth.user.UserMfaFactor
+import io.github.jan.supabase.auth.user.UserSession
import io.github.jan.supabase.testing.assertMethodIs
import io.github.jan.supabase.testing.assertPathIs
import io.github.jan.supabase.testing.createMockedSupabaseClient
diff --git a/GoTrue/src/desktopMain/kotlin/io/github/jan/supabase/gotrue/AuthConfig.kt b/Auth/src/desktopMain/kotlin/io/github/jan/supabase/auth/AuthConfig.kt
similarity index 93%
rename from GoTrue/src/desktopMain/kotlin/io/github/jan/supabase/gotrue/AuthConfig.kt
rename to Auth/src/desktopMain/kotlin/io/github/jan/supabase/auth/AuthConfig.kt
index 2a30aec7d..5304677f1 100644
--- a/GoTrue/src/desktopMain/kotlin/io/github/jan/supabase/gotrue/AuthConfig.kt
+++ b/Auth/src/desktopMain/kotlin/io/github/jan/supabase/auth/AuthConfig.kt
@@ -1,6 +1,6 @@
-package io.github.jan.supabase.gotrue
+package io.github.jan.supabase.auth
-import io.github.jan.supabase.gotrue.server.HttpCallbackHtml
+import io.github.jan.supabase.auth.server.HttpCallbackHtml
import io.github.jan.supabase.plugins.CustomSerializationConfig
import kotlin.time.Duration
import kotlin.time.Duration.Companion.minutes
diff --git a/GoTrue/src/desktopMain/kotlin/io/github/jan/supabase/gotrue/Utils.desktop.kt b/Auth/src/desktopMain/kotlin/io/github/jan/supabase/auth/Utils.desktop.kt
similarity index 79%
rename from GoTrue/src/desktopMain/kotlin/io/github/jan/supabase/gotrue/Utils.desktop.kt
rename to Auth/src/desktopMain/kotlin/io/github/jan/supabase/auth/Utils.desktop.kt
index 818987f8f..2e052c0fe 100644
--- a/GoTrue/src/desktopMain/kotlin/io/github/jan/supabase/gotrue/Utils.desktop.kt
+++ b/Auth/src/desktopMain/kotlin/io/github/jan/supabase/auth/Utils.desktop.kt
@@ -1,7 +1,7 @@
-package io.github.jan.supabase.gotrue
+package io.github.jan.supabase.auth
-import io.github.jan.supabase.gotrue.server.createServer
-import io.github.jan.supabase.gotrue.user.UserSession
+import io.github.jan.supabase.auth.server.createServer
+import io.github.jan.supabase.auth.user.UserSession
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.IO
import kotlinx.coroutines.withContext
diff --git a/GoTrue/src/desktopMain/kotlin/io/github/jan/supabase/gotrue/server/HttpCallbackHtml.kt b/Auth/src/desktopMain/kotlin/io/github/jan/supabase/auth/server/HttpCallbackHtml.kt
similarity index 97%
rename from GoTrue/src/desktopMain/kotlin/io/github/jan/supabase/gotrue/server/HttpCallbackHtml.kt
rename to Auth/src/desktopMain/kotlin/io/github/jan/supabase/auth/server/HttpCallbackHtml.kt
index 0c44d57bf..620cf2564 100644
--- a/GoTrue/src/desktopMain/kotlin/io/github/jan/supabase/gotrue/server/HttpCallbackHtml.kt
+++ b/Auth/src/desktopMain/kotlin/io/github/jan/supabase/auth/server/HttpCallbackHtml.kt
@@ -1,4 +1,4 @@
-package io.github.jan.supabase.gotrue.server
+package io.github.jan.supabase.auth.server
internal object HttpCallbackHtml {
diff --git a/GoTrue/src/desktopMain/kotlin/io/github/jan/supabase/gotrue/server/HttpCallbackRoutes.kt b/Auth/src/desktopMain/kotlin/io/github/jan/supabase/auth/server/HttpCallbackRoutes.kt
similarity index 91%
rename from GoTrue/src/desktopMain/kotlin/io/github/jan/supabase/gotrue/server/HttpCallbackRoutes.kt
rename to Auth/src/desktopMain/kotlin/io/github/jan/supabase/auth/server/HttpCallbackRoutes.kt
index 556916bd7..45c5b6575 100644
--- a/GoTrue/src/desktopMain/kotlin/io/github/jan/supabase/gotrue/server/HttpCallbackRoutes.kt
+++ b/Auth/src/desktopMain/kotlin/io/github/jan/supabase/auth/server/HttpCallbackRoutes.kt
@@ -1,11 +1,10 @@
-package io.github.jan.supabase.gotrue.server
+package io.github.jan.supabase.auth.server
-import io.github.jan.supabase.gotrue.Auth
-import io.github.jan.supabase.gotrue.user.UserSession
+import io.github.jan.supabase.auth.Auth
+import io.github.jan.supabase.auth.user.UserSession
import io.github.jan.supabase.logging.d
import io.ktor.http.ContentType
import io.ktor.http.HttpStatusCode
-import io.ktor.server.application.call
import io.ktor.server.response.respondText
import io.ktor.server.routing.Routing
import io.ktor.server.routing.get
diff --git a/GoTrue/src/desktopMain/kotlin/io/github/jan/supabase/gotrue/server/HttpCallbackServer.kt b/Auth/src/desktopMain/kotlin/io/github/jan/supabase/auth/server/HttpCallbackServer.kt
similarity index 76%
rename from GoTrue/src/desktopMain/kotlin/io/github/jan/supabase/gotrue/server/HttpCallbackServer.kt
rename to Auth/src/desktopMain/kotlin/io/github/jan/supabase/auth/server/HttpCallbackServer.kt
index 990000863..9b4facba6 100644
--- a/GoTrue/src/desktopMain/kotlin/io/github/jan/supabase/gotrue/server/HttpCallbackServer.kt
+++ b/Auth/src/desktopMain/kotlin/io/github/jan/supabase/auth/server/HttpCallbackServer.kt
@@ -1,17 +1,15 @@
-package io.github.jan.supabase.gotrue.server
+package io.github.jan.supabase.auth.server
-import io.github.jan.supabase.annotations.SupabaseExperimental
-import io.github.jan.supabase.gotrue.Auth
-import io.github.jan.supabase.gotrue.AuthImpl
-import io.github.jan.supabase.gotrue.openExternalUrl
-import io.github.jan.supabase.gotrue.user.UserSession
+import io.github.jan.supabase.auth.Auth
+import io.github.jan.supabase.auth.AuthImpl
+import io.github.jan.supabase.auth.openExternalUrl
+import io.github.jan.supabase.auth.user.UserSession
import io.github.jan.supabase.logging.d
import io.ktor.http.ContentType
import io.ktor.http.HttpStatusCode
import io.ktor.server.application.ApplicationCall
import io.ktor.server.application.ApplicationStopPreparing
import io.ktor.server.cio.CIO
-import io.ktor.server.engine.ApplicationEngineEnvironment
import io.ktor.server.engine.embeddedServer
import io.ktor.server.response.respondText
import io.ktor.server.routing.routing
@@ -23,7 +21,6 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlin.coroutines.resume
-@OptIn(SupabaseExperimental::class)
internal suspend fun createServer(
url: suspend (redirect: String) -> String,
auth: Auth,
@@ -41,7 +38,7 @@ internal suspend fun createServer(
}
coroutineScope {
val timeoutScope = launch {
- val port = server.resolvedConnectors().first().port
+ val port = server.engine.resolvedConnectors().first().port
Auth.logger.d {
"Started OAuth callback server on port $port. Opening url in browser..."
}
@@ -64,7 +61,7 @@ internal suspend fun createServer(
}
launch {
suspendCancellableCoroutine {
- server.environment.monitor.subscribe(ApplicationStopPreparing) { _ ->
+ server.monitor.subscribe(ApplicationStopPreparing) { _ ->
it.resume(Unit)
timeoutScope.cancel()
}
@@ -84,14 +81,9 @@ internal suspend fun shutdown(call: ApplicationCall, message: String) {
val latch = CompletableDeferred()
call.application.launch {
+ application.monitor.raise(ApplicationStopPreparing, environment)
latch.join()
-
- environment.monitor.raise(ApplicationStopPreparing, environment)
- if (environment is ApplicationEngineEnvironment) {
- environment.stop()
- } else {
- application.dispose()
- }
+ application.dispose()
}
try {
diff --git a/GoTrue/src/iosMain/kotlin/io/github/jan/supabase/gotrue/AuthConfig.kt b/Auth/src/iosMain/kotlin/io/github/jan/supabase/auth/AuthConfig.kt
similarity index 82%
rename from GoTrue/src/iosMain/kotlin/io/github/jan/supabase/gotrue/AuthConfig.kt
rename to Auth/src/iosMain/kotlin/io/github/jan/supabase/auth/AuthConfig.kt
index 23d7921f0..b7229ecd9 100644
--- a/GoTrue/src/iosMain/kotlin/io/github/jan/supabase/gotrue/AuthConfig.kt
+++ b/Auth/src/iosMain/kotlin/io/github/jan/supabase/auth/AuthConfig.kt
@@ -1,4 +1,4 @@
-package io.github.jan.supabase.gotrue
+package io.github.jan.supabase.auth
import io.github.jan.supabase.plugins.CustomSerializationConfig
diff --git a/GoTrue/src/iosMain/kotlin/io/github/jan/supabase/gotrue/providers/openUrl.kt b/Auth/src/iosMain/kotlin/io/github/jan/supabase/auth/providers/openUrl.kt
similarity index 82%
rename from GoTrue/src/iosMain/kotlin/io/github/jan/supabase/gotrue/providers/openUrl.kt
rename to Auth/src/iosMain/kotlin/io/github/jan/supabase/auth/providers/openUrl.kt
index b7fdaef8a..123aa8a9c 100644
--- a/GoTrue/src/iosMain/kotlin/io/github/jan/supabase/gotrue/providers/openUrl.kt
+++ b/Auth/src/iosMain/kotlin/io/github/jan/supabase/auth/providers/openUrl.kt
@@ -1,6 +1,6 @@
-package io.github.jan.supabase.gotrue.providers
+package io.github.jan.supabase.auth.providers
-import io.github.jan.supabase.gotrue.Auth
+import io.github.jan.supabase.auth.Auth
import io.github.jan.supabase.logging.d
import io.github.jan.supabase.logging.e
import platform.Foundation.NSURL
diff --git a/GoTrue/src/jsMain/kotlin/io/github/jan/supabase/gotrue/AuthConfig.kt b/Auth/src/jsMain/kotlin/io/github/jan/supabase/auth/AuthConfig.kt
similarity index 82%
rename from GoTrue/src/jsMain/kotlin/io/github/jan/supabase/gotrue/AuthConfig.kt
rename to Auth/src/jsMain/kotlin/io/github/jan/supabase/auth/AuthConfig.kt
index 23d7921f0..b7229ecd9 100644
--- a/GoTrue/src/jsMain/kotlin/io/github/jan/supabase/gotrue/AuthConfig.kt
+++ b/Auth/src/jsMain/kotlin/io/github/jan/supabase/auth/AuthConfig.kt
@@ -1,4 +1,4 @@
-package io.github.jan.supabase.gotrue
+package io.github.jan.supabase.auth
import io.github.jan.supabase.plugins.CustomSerializationConfig
diff --git a/GoTrue/src/jsMain/kotlin/io/github/jan/supabase/gotrue/RedirectUrl.js.kt b/Auth/src/jsMain/kotlin/io/github/jan/supabase/auth/RedirectUrl.js.kt
similarity index 83%
rename from GoTrue/src/jsMain/kotlin/io/github/jan/supabase/gotrue/RedirectUrl.js.kt
rename to Auth/src/jsMain/kotlin/io/github/jan/supabase/auth/RedirectUrl.js.kt
index 927ff28a0..9243dbb89 100644
--- a/GoTrue/src/jsMain/kotlin/io/github/jan/supabase/gotrue/RedirectUrl.js.kt
+++ b/Auth/src/jsMain/kotlin/io/github/jan/supabase/auth/RedirectUrl.js.kt
@@ -1,4 +1,4 @@
-package io.github.jan.supabase.gotrue
+package io.github.jan.supabase.auth
import io.github.jan.supabase.annotations.SupabaseInternal
import kotlinx.browser.window
diff --git a/GoTrue/src/jsMain/kotlin/io/github/jan/supabase/gotrue/Utils.js.kt b/Auth/src/jsMain/kotlin/io/github/jan/supabase/auth/Utils.js.kt
similarity index 82%
rename from GoTrue/src/jsMain/kotlin/io/github/jan/supabase/gotrue/Utils.js.kt
rename to Auth/src/jsMain/kotlin/io/github/jan/supabase/auth/Utils.js.kt
index cec38b30a..44cb623db 100644
--- a/GoTrue/src/jsMain/kotlin/io/github/jan/supabase/gotrue/Utils.js.kt
+++ b/Auth/src/jsMain/kotlin/io/github/jan/supabase/auth/Utils.js.kt
@@ -1,4 +1,4 @@
-package io.github.jan.supabase.gotrue
+package io.github.jan.supabase.auth
import io.github.jan.supabase.SupabaseClient
import kotlinx.browser.window
diff --git a/GoTrue/src/appleMain/kotlin/io/github/jan/supabase/gotrue/providers/ExternalAuthConfig.kt b/Auth/src/jsMain/kotlin/io/github/jan/supabase/auth/providers/ExternalAuthConfig.kt
similarity index 76%
rename from GoTrue/src/appleMain/kotlin/io/github/jan/supabase/gotrue/providers/ExternalAuthConfig.kt
rename to Auth/src/jsMain/kotlin/io/github/jan/supabase/auth/providers/ExternalAuthConfig.kt
index de8a14b38..267e6bf68 100644
--- a/GoTrue/src/appleMain/kotlin/io/github/jan/supabase/gotrue/providers/ExternalAuthConfig.kt
+++ b/Auth/src/jsMain/kotlin/io/github/jan/supabase/auth/providers/ExternalAuthConfig.kt
@@ -1,4 +1,4 @@
-package io.github.jan.supabase.gotrue.providers
+package io.github.jan.supabase.auth.providers
/**
* Configuration for external authentication providers like Google, Twitter, etc.
diff --git a/GoTrue/src/jsMain/kotlin/io/github/jan/supabase/gotrue/setupPlatform.kt b/Auth/src/jsMain/kotlin/io/github/jan/supabase/auth/setupPlatform.kt
similarity index 97%
rename from GoTrue/src/jsMain/kotlin/io/github/jan/supabase/gotrue/setupPlatform.kt
rename to Auth/src/jsMain/kotlin/io/github/jan/supabase/auth/setupPlatform.kt
index 45aba1b18..f5eb65a32 100644
--- a/GoTrue/src/jsMain/kotlin/io/github/jan/supabase/gotrue/setupPlatform.kt
+++ b/Auth/src/jsMain/kotlin/io/github/jan/supabase/auth/setupPlatform.kt
@@ -1,4 +1,4 @@
-package io.github.jan.supabase.gotrue
+package io.github.jan.supabase.auth
import io.github.jan.supabase.annotations.SupabaseInternal
import io.ktor.util.PlatformUtils.IS_BROWSER
diff --git a/GoTrue/src/linuxTest/kotlin/platformSettings.kt b/Auth/src/jsTest/kotlin/platformSettings.kt
similarity index 50%
rename from GoTrue/src/linuxTest/kotlin/platformSettings.kt
rename to Auth/src/jsTest/kotlin/platformSettings.kt
index c97cb2bd9..f6fa8ceb8 100644
--- a/GoTrue/src/linuxTest/kotlin/platformSettings.kt
+++ b/Auth/src/jsTest/kotlin/platformSettings.kt
@@ -1,3 +1,3 @@
-import io.github.jan.supabase.gotrue.AuthConfig
+import io.github.jan.supabase.auth.AuthConfig
actual fun AuthConfig.platformSettings() = Unit
\ No newline at end of file
diff --git a/GoTrue/src/jvmMain/kotlin/io/github/jan/supabase/gotrue/RedirectUrl.jvm.kt b/Auth/src/jvmMain/kotlin/io/github/jan/supabase/auth/RedirectUrl.jvm.kt
similarity index 79%
rename from GoTrue/src/jvmMain/kotlin/io/github/jan/supabase/gotrue/RedirectUrl.jvm.kt
rename to Auth/src/jvmMain/kotlin/io/github/jan/supabase/auth/RedirectUrl.jvm.kt
index 86b68e1ed..4b039622f 100644
--- a/GoTrue/src/jvmMain/kotlin/io/github/jan/supabase/gotrue/RedirectUrl.jvm.kt
+++ b/Auth/src/jvmMain/kotlin/io/github/jan/supabase/auth/RedirectUrl.jvm.kt
@@ -1,4 +1,4 @@
-package io.github.jan.supabase.gotrue
+package io.github.jan.supabase.auth
import io.github.jan.supabase.annotations.SupabaseInternal
diff --git a/GoTrue/src/jvmMain/kotlin/io/github/jan/supabase/gotrue/Utils.jvm.kt b/Auth/src/jvmMain/kotlin/io/github/jan/supabase/auth/Utils.jvm.kt
similarity index 90%
rename from GoTrue/src/jvmMain/kotlin/io/github/jan/supabase/gotrue/Utils.jvm.kt
rename to Auth/src/jvmMain/kotlin/io/github/jan/supabase/auth/Utils.jvm.kt
index f5e3261c4..a81e101dc 100644
--- a/GoTrue/src/jvmMain/kotlin/io/github/jan/supabase/gotrue/Utils.jvm.kt
+++ b/Auth/src/jvmMain/kotlin/io/github/jan/supabase/auth/Utils.jvm.kt
@@ -1,4 +1,4 @@
-package io.github.jan.supabase.gotrue
+package io.github.jan.supabase.auth
import io.github.jan.supabase.SupabaseClient
import kotlinx.coroutines.Dispatchers
diff --git a/GoTrue/src/androidMain/kotlin/io/github/jan/supabase/gotrue/providers/ExternalAuthConfig.kt b/Auth/src/jvmMain/kotlin/io/github/jan/supabase/auth/providers/ExternalAuthConfig.kt
similarity index 76%
rename from GoTrue/src/androidMain/kotlin/io/github/jan/supabase/gotrue/providers/ExternalAuthConfig.kt
rename to Auth/src/jvmMain/kotlin/io/github/jan/supabase/auth/providers/ExternalAuthConfig.kt
index de8a14b38..267e6bf68 100644
--- a/GoTrue/src/androidMain/kotlin/io/github/jan/supabase/gotrue/providers/ExternalAuthConfig.kt
+++ b/Auth/src/jvmMain/kotlin/io/github/jan/supabase/auth/providers/ExternalAuthConfig.kt
@@ -1,4 +1,4 @@
-package io.github.jan.supabase.gotrue.providers
+package io.github.jan.supabase.auth.providers
/**
* Configuration for external authentication providers like Google, Twitter, etc.
diff --git a/GoTrue/src/appleMain/kotlin/io/github/jan/supabase/gotrue/setupPlatform.kt b/Auth/src/jvmMain/kotlin/io/github/jan/supabase/auth/setupPlatform.kt
similarity index 75%
rename from GoTrue/src/appleMain/kotlin/io/github/jan/supabase/gotrue/setupPlatform.kt
rename to Auth/src/jvmMain/kotlin/io/github/jan/supabase/auth/setupPlatform.kt
index 95b106316..a1b155557 100644
--- a/GoTrue/src/appleMain/kotlin/io/github/jan/supabase/gotrue/setupPlatform.kt
+++ b/Auth/src/jvmMain/kotlin/io/github/jan/supabase/auth/setupPlatform.kt
@@ -1,4 +1,4 @@
-package io.github.jan.supabase.gotrue
+package io.github.jan.supabase.auth
import io.github.jan.supabase.annotations.SupabaseInternal
diff --git a/GoTrue/src/jsTest/kotlin/platformSettings.kt b/Auth/src/jvmTest/kotlin/platformSettings.kt
similarity index 50%
rename from GoTrue/src/jsTest/kotlin/platformSettings.kt
rename to Auth/src/jvmTest/kotlin/platformSettings.kt
index c97cb2bd9..f6fa8ceb8 100644
--- a/GoTrue/src/jsTest/kotlin/platformSettings.kt
+++ b/Auth/src/jvmTest/kotlin/platformSettings.kt
@@ -1,3 +1,3 @@
-import io.github.jan.supabase.gotrue.AuthConfig
+import io.github.jan.supabase.auth.AuthConfig
actual fun AuthConfig.platformSettings() = Unit
\ No newline at end of file
diff --git a/GoTrue/src/linuxMain/kotlin/io/github/jan/supabase/gotrue/SettingsUtil.kt b/Auth/src/linuxMain/kotlin/io/github/jan/supabase/auth/SettingsUtil.kt
similarity index 88%
rename from GoTrue/src/linuxMain/kotlin/io/github/jan/supabase/gotrue/SettingsUtil.kt
rename to Auth/src/linuxMain/kotlin/io/github/jan/supabase/auth/SettingsUtil.kt
index fde7e333a..30573d808 100644
--- a/GoTrue/src/linuxMain/kotlin/io/github/jan/supabase/gotrue/SettingsUtil.kt
+++ b/Auth/src/linuxMain/kotlin/io/github/jan/supabase/auth/SettingsUtil.kt
@@ -1,4 +1,4 @@
-package io.github.jan.supabase.gotrue
+package io.github.jan.supabase.auth
import io.github.jan.supabase.annotations.SupabaseInternal
diff --git a/GoTrue/src/linuxMain/kotlin/io/github/jan/supabase/gotrue/Utils.linux.kt b/Auth/src/linuxMain/kotlin/io/github/jan/supabase/auth/Utils.linux.kt
similarity index 82%
rename from GoTrue/src/linuxMain/kotlin/io/github/jan/supabase/gotrue/Utils.linux.kt
rename to Auth/src/linuxMain/kotlin/io/github/jan/supabase/auth/Utils.linux.kt
index 4580f9005..6594987ea 100644
--- a/GoTrue/src/linuxMain/kotlin/io/github/jan/supabase/gotrue/Utils.linux.kt
+++ b/Auth/src/linuxMain/kotlin/io/github/jan/supabase/auth/Utils.linux.kt
@@ -1,4 +1,4 @@
-package io.github.jan.supabase.gotrue
+package io.github.jan.supabase.auth
import io.github.jan.supabase.SupabaseClient
import platform.posix.system
diff --git a/GoTrue/src/mingwMain/kotlin/io/github/jan/supabase/gotrue/generateRedirectUrl.kt b/Auth/src/linuxMain/kotlin/io/github/jan/supabase/auth/generateRedirectUrl.kt
similarity index 79%
rename from GoTrue/src/mingwMain/kotlin/io/github/jan/supabase/gotrue/generateRedirectUrl.kt
rename to Auth/src/linuxMain/kotlin/io/github/jan/supabase/auth/generateRedirectUrl.kt
index 86b68e1ed..4b039622f 100644
--- a/GoTrue/src/mingwMain/kotlin/io/github/jan/supabase/gotrue/generateRedirectUrl.kt
+++ b/Auth/src/linuxMain/kotlin/io/github/jan/supabase/auth/generateRedirectUrl.kt
@@ -1,4 +1,4 @@
-package io.github.jan.supabase.gotrue
+package io.github.jan.supabase.auth
import io.github.jan.supabase.annotations.SupabaseInternal
diff --git a/Auth/src/linuxMain/kotlin/io/github/jan/supabase/auth/providers/ExternalAuthConfig.kt b/Auth/src/linuxMain/kotlin/io/github/jan/supabase/auth/providers/ExternalAuthConfig.kt
new file mode 100644
index 000000000..267e6bf68
--- /dev/null
+++ b/Auth/src/linuxMain/kotlin/io/github/jan/supabase/auth/providers/ExternalAuthConfig.kt
@@ -0,0 +1,6 @@
+package io.github.jan.supabase.auth.providers
+
+/**
+ * Configuration for external authentication providers like Google, Twitter, etc.
+ */
+actual class ExternalAuthConfig: ExternalAuthConfigDefaults()
\ No newline at end of file
diff --git a/GoTrue/src/linuxMain/kotlin/io/github/jan/supabase/gotrue/setupPlatform.kt b/Auth/src/linuxMain/kotlin/io/github/jan/supabase/auth/setupPlatform.kt
similarity index 86%
rename from GoTrue/src/linuxMain/kotlin/io/github/jan/supabase/gotrue/setupPlatform.kt
rename to Auth/src/linuxMain/kotlin/io/github/jan/supabase/auth/setupPlatform.kt
index d20682368..3d6b9decc 100644
--- a/GoTrue/src/linuxMain/kotlin/io/github/jan/supabase/gotrue/setupPlatform.kt
+++ b/Auth/src/linuxMain/kotlin/io/github/jan/supabase/auth/setupPlatform.kt
@@ -1,4 +1,4 @@
-package io.github.jan.supabase.gotrue
+package io.github.jan.supabase.auth
import io.github.jan.supabase.annotations.SupabaseInternal
import io.github.jan.supabase.logging.w
diff --git a/GoTrue/src/appleTest/kotlin/platformSettings.kt b/Auth/src/linuxTest/kotlin/platformSettings.kt
similarity index 50%
rename from GoTrue/src/appleTest/kotlin/platformSettings.kt
rename to Auth/src/linuxTest/kotlin/platformSettings.kt
index c97cb2bd9..f6fa8ceb8 100644
--- a/GoTrue/src/appleTest/kotlin/platformSettings.kt
+++ b/Auth/src/linuxTest/kotlin/platformSettings.kt
@@ -1,3 +1,3 @@
-import io.github.jan.supabase.gotrue.AuthConfig
+import io.github.jan.supabase.auth.AuthConfig
actual fun AuthConfig.platformSettings() = Unit
\ No newline at end of file
diff --git a/GoTrue/src/macosMain/kotlin/io/github/jan/supabase/gotrue/providers/openUrl.kt b/Auth/src/macosMain/kotlin/io/github/jan/supabase/auth/providers/openUrl.kt
similarity index 84%
rename from GoTrue/src/macosMain/kotlin/io/github/jan/supabase/gotrue/providers/openUrl.kt
rename to Auth/src/macosMain/kotlin/io/github/jan/supabase/auth/providers/openUrl.kt
index 96de97dd7..3ff87b96b 100644
--- a/GoTrue/src/macosMain/kotlin/io/github/jan/supabase/gotrue/providers/openUrl.kt
+++ b/Auth/src/macosMain/kotlin/io/github/jan/supabase/auth/providers/openUrl.kt
@@ -1,6 +1,6 @@
-package io.github.jan.supabase.gotrue.providers
+package io.github.jan.supabase.auth.providers
-import io.github.jan.supabase.gotrue.Auth
+import io.github.jan.supabase.auth.Auth
import io.github.jan.supabase.logging.d
import io.github.jan.supabase.logging.e
import platform.AppKit.NSWorkspace
diff --git a/GoTrue/src/mingwMain/kotlin/io/github/jan/supabase/gotrue/AuthConfig.kt b/Auth/src/mingwMain/kotlin/io/github/jan/supabase/auth/AuthConfig.kt
similarity index 78%
rename from GoTrue/src/mingwMain/kotlin/io/github/jan/supabase/gotrue/AuthConfig.kt
rename to Auth/src/mingwMain/kotlin/io/github/jan/supabase/auth/AuthConfig.kt
index ecd2559a0..3d476bd2e 100644
--- a/GoTrue/src/mingwMain/kotlin/io/github/jan/supabase/gotrue/AuthConfig.kt
+++ b/Auth/src/mingwMain/kotlin/io/github/jan/supabase/auth/AuthConfig.kt
@@ -1,4 +1,4 @@
-package io.github.jan.supabase.gotrue
+package io.github.jan.supabase.auth
import io.github.jan.supabase.plugins.CustomSerializationConfig
diff --git a/GoTrue/src/mingwMain/kotlin/io/github/jan/supabase/gotrue/Utils.mingw.kt b/Auth/src/mingwMain/kotlin/io/github/jan/supabase/auth/Utils.mingw.kt
similarity index 90%
rename from GoTrue/src/mingwMain/kotlin/io/github/jan/supabase/gotrue/Utils.mingw.kt
rename to Auth/src/mingwMain/kotlin/io/github/jan/supabase/auth/Utils.mingw.kt
index b741c818f..1c197eb52 100644
--- a/GoTrue/src/mingwMain/kotlin/io/github/jan/supabase/gotrue/Utils.mingw.kt
+++ b/Auth/src/mingwMain/kotlin/io/github/jan/supabase/auth/Utils.mingw.kt
@@ -1,4 +1,4 @@
-package io.github.jan.supabase.gotrue
+package io.github.jan.supabase.auth
import io.github.jan.supabase.SupabaseClient
import kotlinx.cinterop.ExperimentalForeignApi
diff --git a/GoTrue/src/linuxMain/kotlin/io/github/jan/supabase/gotrue/generateRedirectUrl.kt b/Auth/src/mingwMain/kotlin/io/github/jan/supabase/auth/generateRedirectUrl.kt
similarity index 79%
rename from GoTrue/src/linuxMain/kotlin/io/github/jan/supabase/gotrue/generateRedirectUrl.kt
rename to Auth/src/mingwMain/kotlin/io/github/jan/supabase/auth/generateRedirectUrl.kt
index 86b68e1ed..4b039622f 100644
--- a/GoTrue/src/linuxMain/kotlin/io/github/jan/supabase/gotrue/generateRedirectUrl.kt
+++ b/Auth/src/mingwMain/kotlin/io/github/jan/supabase/auth/generateRedirectUrl.kt
@@ -1,4 +1,4 @@
-package io.github.jan.supabase.gotrue
+package io.github.jan.supabase.auth
import io.github.jan.supabase.annotations.SupabaseInternal
diff --git a/GoTrue/src/mingwMain/kotlin/io/github/jan/supabase/gotrue/providers/ExternalAuthConfig.kt b/Auth/src/mingwMain/kotlin/io/github/jan/supabase/auth/providers/ExternalAuthConfig.kt
similarity index 76%
rename from GoTrue/src/mingwMain/kotlin/io/github/jan/supabase/gotrue/providers/ExternalAuthConfig.kt
rename to Auth/src/mingwMain/kotlin/io/github/jan/supabase/auth/providers/ExternalAuthConfig.kt
index 55fea02d6..990f41387 100644
--- a/GoTrue/src/mingwMain/kotlin/io/github/jan/supabase/gotrue/providers/ExternalAuthConfig.kt
+++ b/Auth/src/mingwMain/kotlin/io/github/jan/supabase/auth/providers/ExternalAuthConfig.kt
@@ -1,4 +1,4 @@
-package io.github.jan.supabase.gotrue.providers
+package io.github.jan.supabase.auth.providers
/**
* Configuration for external authentication providers like Google, Twitter, etc.
diff --git a/GoTrue/src/mingwMain/kotlin/io/github/jan/supabase/gotrue/setupPlatform.kt b/Auth/src/mingwMain/kotlin/io/github/jan/supabase/auth/setupPlatform.kt
similarity index 86%
rename from GoTrue/src/mingwMain/kotlin/io/github/jan/supabase/gotrue/setupPlatform.kt
rename to Auth/src/mingwMain/kotlin/io/github/jan/supabase/auth/setupPlatform.kt
index eb7c34bf1..61ef450fd 100644
--- a/GoTrue/src/mingwMain/kotlin/io/github/jan/supabase/gotrue/setupPlatform.kt
+++ b/Auth/src/mingwMain/kotlin/io/github/jan/supabase/auth/setupPlatform.kt
@@ -1,4 +1,4 @@
-package io.github.jan.supabase.gotrue
+package io.github.jan.supabase.auth
import io.github.jan.supabase.annotations.SupabaseInternal
import io.github.jan.supabase.logging.w
diff --git a/Auth/src/mingwTest/kotlin/platformSettings.kt b/Auth/src/mingwTest/kotlin/platformSettings.kt
new file mode 100644
index 000000000..f6fa8ceb8
--- /dev/null
+++ b/Auth/src/mingwTest/kotlin/platformSettings.kt
@@ -0,0 +1,3 @@
+import io.github.jan.supabase.auth.AuthConfig
+
+actual fun AuthConfig.platformSettings() = Unit
\ No newline at end of file
diff --git a/GoTrue/src/nonDesktopMain/kotlin/io/github/jan/supabase/gotrue/Utils.kt b/Auth/src/nonDesktopMain/kotlin/io/github/jan/supabase/auth/Utils.kt
similarity index 75%
rename from GoTrue/src/nonDesktopMain/kotlin/io/github/jan/supabase/gotrue/Utils.kt
rename to Auth/src/nonDesktopMain/kotlin/io/github/jan/supabase/auth/Utils.kt
index 7e68cf845..5f0b7cafa 100644
--- a/GoTrue/src/nonDesktopMain/kotlin/io/github/jan/supabase/gotrue/Utils.kt
+++ b/Auth/src/nonDesktopMain/kotlin/io/github/jan/supabase/auth/Utils.kt
@@ -1,7 +1,7 @@
@file:Suppress("RedundantSuspendModifier")
-package io.github.jan.supabase.gotrue
+package io.github.jan.supabase.auth
-import io.github.jan.supabase.gotrue.user.UserSession
+import io.github.jan.supabase.auth.user.UserSession
internal actual suspend fun Auth.startExternalAuth(
redirectUrl: String?,
diff --git a/GoTrue/src/settingsMain/kotlin/io/github/jan/supabase/gotrue/SettingsCodeVerifierCache.kt b/Auth/src/settingsMain/kotlin/io/github/jan/supabase/auth/SettingsCodeVerifierCache.kt
similarity index 86%
rename from GoTrue/src/settingsMain/kotlin/io/github/jan/supabase/gotrue/SettingsCodeVerifierCache.kt
rename to Auth/src/settingsMain/kotlin/io/github/jan/supabase/auth/SettingsCodeVerifierCache.kt
index 72eb75526..418bbac59 100644
--- a/GoTrue/src/settingsMain/kotlin/io/github/jan/supabase/gotrue/SettingsCodeVerifierCache.kt
+++ b/Auth/src/settingsMain/kotlin/io/github/jan/supabase/auth/SettingsCodeVerifierCache.kt
@@ -1,4 +1,4 @@
-package io.github.jan.supabase.gotrue
+package io.github.jan.supabase.auth
import com.russhwolf.settings.ExperimentalSettingsApi
import com.russhwolf.settings.Settings
@@ -10,6 +10,7 @@ import com.russhwolf.settings.coroutines.toSuspendSettings
* @param settings The [Settings] instance to use. Defaults to [createDefaultSettings].
* @param key The key to use for saving the code verifier.
*/
+@OptIn(ExperimentalSettingsApi::class)
class SettingsCodeVerifierCache(
private val settings: Settings = createDefaultSettings(),
private val key: String = SETTINGS_KEY,
@@ -31,20 +32,16 @@ class SettingsCodeVerifierCache(
}
}
- @OptIn(ExperimentalSettingsApi::class)
private val suspendSettings = settings.toSuspendSettings()
- @OptIn(ExperimentalSettingsApi::class, ExperimentalSettingsApi::class)
override suspend fun saveCodeVerifier(codeVerifier: String) {
suspendSettings.putString(key, codeVerifier)
}
- @OptIn(ExperimentalSettingsApi::class)
override suspend fun loadCodeVerifier(): String? {
return suspendSettings.getStringOrNull(key)
}
- @OptIn(ExperimentalSettingsApi::class)
override suspend fun deleteCodeVerifier() {
suspendSettings.remove(key)
}
diff --git a/GoTrue/src/settingsMain/kotlin/io/github/jan/supabase/gotrue/SettingsSessionManager.kt b/Auth/src/settingsMain/kotlin/io/github/jan/supabase/auth/SettingsSessionManager.kt
similarity index 90%
rename from GoTrue/src/settingsMain/kotlin/io/github/jan/supabase/gotrue/SettingsSessionManager.kt
rename to Auth/src/settingsMain/kotlin/io/github/jan/supabase/auth/SettingsSessionManager.kt
index e924fa118..7d22aae95 100644
--- a/GoTrue/src/settingsMain/kotlin/io/github/jan/supabase/gotrue/SettingsSessionManager.kt
+++ b/Auth/src/settingsMain/kotlin/io/github/jan/supabase/auth/SettingsSessionManager.kt
@@ -1,9 +1,9 @@
-package io.github.jan.supabase.gotrue
+package io.github.jan.supabase.auth
import com.russhwolf.settings.ExperimentalSettingsApi
import com.russhwolf.settings.Settings
import com.russhwolf.settings.coroutines.toSuspendSettings
-import io.github.jan.supabase.gotrue.user.UserSession
+import io.github.jan.supabase.auth.user.UserSession
import io.github.jan.supabase.logging.e
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
@@ -20,6 +20,7 @@ private val settingsJson = Json {
* @param key The key to use for saving the session.
* @param json The [Json] instance to use for serialization. Defaults to [settingsJson]. **Important: [JsonBuilder.encodeDefaults] must be set to true.**
*/
+@OptIn(ExperimentalSettingsApi::class)
class SettingsSessionManager(
private val settings: Settings = createDefaultSettings(),
private val key: String = SETTINGS_KEY,
@@ -44,12 +45,10 @@ class SettingsSessionManager(
private val suspendSettings = settings.toSuspendSettings()
- @OptIn(ExperimentalSettingsApi::class)
override suspend fun saveSession(session: UserSession) {
suspendSettings.putString(key, json.encodeToString(session))
}
- @OptIn(ExperimentalSettingsApi::class)
override suspend fun loadSession(): UserSession? {
val session = suspendSettings.getStringOrNull(key) ?: return null
return try {
@@ -60,7 +59,6 @@ class SettingsSessionManager(
}
}
- @OptIn(ExperimentalSettingsApi::class)
override suspend fun deleteSession() {
suspendSettings.remove(key)
}
diff --git a/GoTrue/src/settingsMain/kotlin/io/github/jan/supabase/gotrue/SettingsUtil.kt b/Auth/src/settingsMain/kotlin/io/github/jan/supabase/auth/SettingsUtil.kt
similarity index 97%
rename from GoTrue/src/settingsMain/kotlin/io/github/jan/supabase/gotrue/SettingsUtil.kt
rename to Auth/src/settingsMain/kotlin/io/github/jan/supabase/auth/SettingsUtil.kt
index d27d661d0..1b9d4b138 100644
--- a/GoTrue/src/settingsMain/kotlin/io/github/jan/supabase/gotrue/SettingsUtil.kt
+++ b/Auth/src/settingsMain/kotlin/io/github/jan/supabase/auth/SettingsUtil.kt
@@ -1,4 +1,4 @@
-package io.github.jan.supabase.gotrue
+package io.github.jan.supabase.auth
import com.russhwolf.settings.Settings
import io.github.jan.supabase.annotations.SupabaseInternal
diff --git a/GoTrue/src/settingsTest/kotlin/SettingsCodeVerifierCacheTest.kt b/Auth/src/settingsTest/kotlin/SettingsCodeVerifierCacheTest.kt
similarity index 95%
rename from GoTrue/src/settingsTest/kotlin/SettingsCodeVerifierCacheTest.kt
rename to Auth/src/settingsTest/kotlin/SettingsCodeVerifierCacheTest.kt
index fd636b0d3..6a3e5fef7 100644
--- a/GoTrue/src/settingsTest/kotlin/SettingsCodeVerifierCacheTest.kt
+++ b/Auth/src/settingsTest/kotlin/SettingsCodeVerifierCacheTest.kt
@@ -1,5 +1,5 @@
import com.russhwolf.settings.MapSettings
-import io.github.jan.supabase.gotrue.SettingsCodeVerifierCache
+import io.github.jan.supabase.auth.SettingsCodeVerifierCache
import kotlinx.coroutines.test.runTest
import kotlin.test.Test
import kotlin.test.assertEquals
diff --git a/GoTrue/src/settingsTest/kotlin/SettingsSessionManagerTest.kt b/Auth/src/settingsTest/kotlin/SettingsSessionManagerTest.kt
similarity index 94%
rename from GoTrue/src/settingsTest/kotlin/SettingsSessionManagerTest.kt
rename to Auth/src/settingsTest/kotlin/SettingsSessionManagerTest.kt
index 68c5440f2..0139c01b9 100644
--- a/GoTrue/src/settingsTest/kotlin/SettingsSessionManagerTest.kt
+++ b/Auth/src/settingsTest/kotlin/SettingsSessionManagerTest.kt
@@ -1,6 +1,6 @@
import com.russhwolf.settings.MapSettings
-import io.github.jan.supabase.gotrue.SettingsSessionManager
-import io.github.jan.supabase.gotrue.user.UserSession
+import io.github.jan.supabase.auth.SettingsSessionManager
+import io.github.jan.supabase.auth.user.UserSession
import kotlinx.coroutines.test.runTest
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
diff --git a/GoTrue/src/settingsTest/kotlin/SettingsTest.kt b/Auth/src/settingsTest/kotlin/SettingsTest.kt
similarity index 81%
rename from GoTrue/src/settingsTest/kotlin/SettingsTest.kt
rename to Auth/src/settingsTest/kotlin/SettingsTest.kt
index 33df27f51..a2bef23a8 100644
--- a/GoTrue/src/settingsTest/kotlin/SettingsTest.kt
+++ b/Auth/src/settingsTest/kotlin/SettingsTest.kt
@@ -1,4 +1,4 @@
-import io.github.jan.supabase.gotrue.createDefaultSettingsKey
+import io.github.jan.supabase.auth.createDefaultSettingsKey
import kotlin.test.Test
import kotlin.test.assertEquals
diff --git a/GoTrue/src/watchosMain/kotlin/io/github/jan/supabase/gotrue/AuthConfig.kt b/Auth/src/tvosMain/kotlin/io/github/jan/supabase/auth/AuthConfig.kt
similarity index 82%
rename from GoTrue/src/watchosMain/kotlin/io/github/jan/supabase/gotrue/AuthConfig.kt
rename to Auth/src/tvosMain/kotlin/io/github/jan/supabase/auth/AuthConfig.kt
index 23d7921f0..b7229ecd9 100644
--- a/GoTrue/src/watchosMain/kotlin/io/github/jan/supabase/gotrue/AuthConfig.kt
+++ b/Auth/src/tvosMain/kotlin/io/github/jan/supabase/auth/AuthConfig.kt
@@ -1,4 +1,4 @@
-package io.github.jan.supabase.gotrue
+package io.github.jan.supabase.auth
import io.github.jan.supabase.plugins.CustomSerializationConfig
diff --git a/GoTrue/src/tvosMain/kotlin/io/github/jan/supabase/gotrue/providers/openUrl.kt b/Auth/src/tvosMain/kotlin/io/github/jan/supabase/auth/providers/openUrl.kt
similarity index 70%
rename from GoTrue/src/tvosMain/kotlin/io/github/jan/supabase/gotrue/providers/openUrl.kt
rename to Auth/src/tvosMain/kotlin/io/github/jan/supabase/auth/providers/openUrl.kt
index 989d66799..471b13d3c 100644
--- a/GoTrue/src/tvosMain/kotlin/io/github/jan/supabase/gotrue/providers/openUrl.kt
+++ b/Auth/src/tvosMain/kotlin/io/github/jan/supabase/auth/providers/openUrl.kt
@@ -1,4 +1,4 @@
-package io.github.jan.supabase.gotrue.providers
+package io.github.jan.supabase.auth.providers
import platform.Foundation.NSURL
diff --git a/Auth/src/wasmJsMain/kotlin/io/github/jan/supabase/auth/AuthConfig.kt b/Auth/src/wasmJsMain/kotlin/io/github/jan/supabase/auth/AuthConfig.kt
new file mode 100644
index 000000000..9fd9994f5
--- /dev/null
+++ b/Auth/src/wasmJsMain/kotlin/io/github/jan/supabase/auth/AuthConfig.kt
@@ -0,0 +1,8 @@
+package io.github.jan.supabase.auth
+
+import io.github.jan.supabase.plugins.CustomSerializationConfig
+
+/**
+ * The configuration for [Auth]
+ */
+actual class AuthConfig: CustomSerializationConfig, AuthConfigDefaults()
\ No newline at end of file
diff --git a/Auth/src/wasmJsMain/kotlin/io/github/jan/supabase/auth/RedirectUrl.kt b/Auth/src/wasmJsMain/kotlin/io/github/jan/supabase/auth/RedirectUrl.kt
new file mode 100644
index 000000000..1ad88fcf5
--- /dev/null
+++ b/Auth/src/wasmJsMain/kotlin/io/github/jan/supabase/auth/RedirectUrl.kt
@@ -0,0 +1,7 @@
+package io.github.jan.supabase.auth
+
+import io.github.jan.supabase.annotations.SupabaseInternal
+import kotlinx.browser.window
+
+@SupabaseInternal
+actual fun Auth.defaultPlatformRedirectUrl(): String? = window.location.origin
\ No newline at end of file
diff --git a/Auth/src/wasmJsMain/kotlin/io/github/jan/supabase/auth/Utils.wasmJs.kt b/Auth/src/wasmJsMain/kotlin/io/github/jan/supabase/auth/Utils.wasmJs.kt
new file mode 100644
index 000000000..44cb623db
--- /dev/null
+++ b/Auth/src/wasmJsMain/kotlin/io/github/jan/supabase/auth/Utils.wasmJs.kt
@@ -0,0 +1,8 @@
+package io.github.jan.supabase.auth
+
+import io.github.jan.supabase.SupabaseClient
+import kotlinx.browser.window
+
+internal actual suspend fun SupabaseClient.openExternalUrl(url: String) {
+ window.location.href = url
+}
\ No newline at end of file
diff --git a/Auth/src/wasmJsMain/kotlin/io/github/jan/supabase/auth/providers/ExternalAuthConfig.kt b/Auth/src/wasmJsMain/kotlin/io/github/jan/supabase/auth/providers/ExternalAuthConfig.kt
new file mode 100644
index 000000000..267e6bf68
--- /dev/null
+++ b/Auth/src/wasmJsMain/kotlin/io/github/jan/supabase/auth/providers/ExternalAuthConfig.kt
@@ -0,0 +1,6 @@
+package io.github.jan.supabase.auth.providers
+
+/**
+ * Configuration for external authentication providers like Google, Twitter, etc.
+ */
+actual class ExternalAuthConfig: ExternalAuthConfigDefaults()
\ No newline at end of file
diff --git a/Auth/src/wasmJsMain/kotlin/io/github/jan/supabase/auth/setupPlatform.kt b/Auth/src/wasmJsMain/kotlin/io/github/jan/supabase/auth/setupPlatform.kt
new file mode 100644
index 000000000..8137057b1
--- /dev/null
+++ b/Auth/src/wasmJsMain/kotlin/io/github/jan/supabase/auth/setupPlatform.kt
@@ -0,0 +1,47 @@
+package io.github.jan.supabase.auth
+
+import io.github.jan.supabase.annotations.SupabaseInternal
+import io.ktor.util.PlatformUtils.IS_BROWSER
+import kotlinx.browser.window
+import kotlinx.coroutines.launch
+import org.w3c.dom.url.URL
+
+@SupabaseInternal
+actual fun Auth.setupPlatform() {
+ this as AuthImpl
+
+ fun checkForHash() {
+ if(window.location.hash.isBlank()) return
+ val afterHash = window.location.hash.substring(1)
+
+ if(!afterHash.contains('=')) {
+ // No params after hash, no need to continue
+ return
+ }
+ parseFragmentAndImportSession(afterHash) {
+ val newURL = window.location.href.split("?")[0];
+ window.history.replaceState(null, window.document.title, newURL);
+ }
+ }
+
+ fun checkForPCKECode() {
+ val url = URL(window.location.href)
+ val code = url.searchParams.get("code") ?: return
+ authScope.launch {
+ val session = exchangeCodeForSession(code)
+ importSession(session)
+ }
+ val newURL = window.location.href.split("?")[0];
+ window.history.replaceState(null, window.document.title, newURL);
+ }
+
+ if(IS_BROWSER) {
+ window.onhashchange = {
+ checkForHash()
+ }
+ window.onload = {
+ checkForHash()
+ checkForPCKECode()
+ }
+ }
+}
\ No newline at end of file
diff --git a/Auth/src/wasmJsTest/kotlin/platformSettings.kt b/Auth/src/wasmJsTest/kotlin/platformSettings.kt
new file mode 100644
index 000000000..f6fa8ceb8
--- /dev/null
+++ b/Auth/src/wasmJsTest/kotlin/platformSettings.kt
@@ -0,0 +1,3 @@
+import io.github.jan.supabase.auth.AuthConfig
+
+actual fun AuthConfig.platformSettings() = Unit
\ No newline at end of file
diff --git a/GoTrue/src/tvosMain/kotlin/io/github/jan/supabase/gotrue/AuthConfig.kt b/Auth/src/watchosMain/kotlin/io/github/jan/supabase/auth/AuthConfig.kt
similarity index 82%
rename from GoTrue/src/tvosMain/kotlin/io/github/jan/supabase/gotrue/AuthConfig.kt
rename to Auth/src/watchosMain/kotlin/io/github/jan/supabase/auth/AuthConfig.kt
index 23d7921f0..b7229ecd9 100644
--- a/GoTrue/src/tvosMain/kotlin/io/github/jan/supabase/gotrue/AuthConfig.kt
+++ b/Auth/src/watchosMain/kotlin/io/github/jan/supabase/auth/AuthConfig.kt
@@ -1,4 +1,4 @@
-package io.github.jan.supabase.gotrue
+package io.github.jan.supabase.auth
import io.github.jan.supabase.plugins.CustomSerializationConfig
diff --git a/GoTrue/src/watchosMain/kotlin/io/github/jan/supabase/gotrue/providers/openUrl.kt b/Auth/src/watchosMain/kotlin/io/github/jan/supabase/auth/providers/openUrl.kt
similarity index 70%
rename from GoTrue/src/watchosMain/kotlin/io/github/jan/supabase/gotrue/providers/openUrl.kt
rename to Auth/src/watchosMain/kotlin/io/github/jan/supabase/auth/providers/openUrl.kt
index f87562bc7..d7e2f9131 100644
--- a/GoTrue/src/watchosMain/kotlin/io/github/jan/supabase/gotrue/providers/openUrl.kt
+++ b/Auth/src/watchosMain/kotlin/io/github/jan/supabase/auth/providers/openUrl.kt
@@ -1,4 +1,4 @@
-package io.github.jan.supabase.gotrue.providers
+package io.github.jan.supabase.auth.providers
import platform.Foundation.NSURL
diff --git a/Functions/README.md b/Functions/README.md
index 8324f6c53..55374b781 100644
--- a/Functions/README.md
+++ b/Functions/README.md
@@ -4,23 +4,27 @@ Extends Supabase-kt with a multiplatform Functions client.
Supported targets:
-| Target | **JVM** | **Android** | **JS** | **iOS** | **tvOS** | **watchOS** | **macOS** | **Windows** | **Linux** |
-|--------|---------|-------------|--------|---------|----------|-------------|-----------|-------------|-----------|
-| | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
+| Target | **JVM** | **Android** | **JS** | **Wasm** | **Apple** | **Windows** | **Linux** |
+|--------|---------|-------------|--------|----------|-----------|-------------|-----------|
+| Status | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
In-depth Kotlin targets
-**iOS:** iosArm64, iosSimulatorArm64, iosX64
-
**JS**: Browser, NodeJS
-**tvOS**: tvosArm64, tvosX64, tvosSimulatorArm64
+**Wasm**: wasm-js
+
+**Apple:**
+
+- iOS: iosArm64, iosSimulatorArm64, iosX64
+
+- tvOS: tvosArm64, tvosX64, tvosSimulatorArm64
-**watchOS**: watchosArm64, watchosX64, watchosSimulatorArm64
+- watchOS: watchosArm64, watchosX64, watchosSimulatorArm64
-**MacOS**: macosX64, macosArm64
+- MacOS: macosX64, macosArm64
**Windows**: mingwX64
diff --git a/Functions/build.gradle.kts b/Functions/build.gradle.kts
index a5837c36b..324abd7fa 100644
--- a/Functions/build.gradle.kts
+++ b/Functions/build.gradle.kts
@@ -15,7 +15,7 @@ kotlin {
sourceSets {
val commonMain by getting {
dependencies {
- addModules(SupabaseModule.GOTRUE, SupabaseModule.SUPABASE)
+ addModules(SupabaseModule.AUTH, SupabaseModule.SUPABASE)
}
}
val commonTest by getting {
diff --git a/Functions/src/commonMain/kotlin/io/github/jan/supabase/functions/FunctionRegion.kt b/Functions/src/commonMain/kotlin/io/github/jan/supabase/functions/FunctionRegion.kt
index 67d39f863..8219a921f 100644
--- a/Functions/src/commonMain/kotlin/io/github/jan/supabase/functions/FunctionRegion.kt
+++ b/Functions/src/commonMain/kotlin/io/github/jan/supabase/functions/FunctionRegion.kt
@@ -1,3 +1,4 @@
+@file:Suppress("UndocumentedPublicProperty")
package io.github.jan.supabase.functions
/**
diff --git a/Functions/src/commonMain/kotlin/io/github/jan/supabase/functions/Functions.kt b/Functions/src/commonMain/kotlin/io/github/jan/supabase/functions/Functions.kt
index fcc807269..752f9f1f4 100644
--- a/Functions/src/commonMain/kotlin/io/github/jan/supabase/functions/Functions.kt
+++ b/Functions/src/commonMain/kotlin/io/github/jan/supabase/functions/Functions.kt
@@ -3,13 +3,13 @@ package io.github.jan.supabase.functions
import io.github.jan.supabase.SupabaseClient
import io.github.jan.supabase.SupabaseSerializer
import io.github.jan.supabase.annotations.SupabaseInternal
+import io.github.jan.supabase.auth.Auth
+import io.github.jan.supabase.auth.authenticatedSupabaseApi
import io.github.jan.supabase.encode
import io.github.jan.supabase.exceptions.BadRequestRestException
import io.github.jan.supabase.exceptions.NotFoundRestException
import io.github.jan.supabase.exceptions.RestException
import io.github.jan.supabase.exceptions.UnauthorizedRestException
-import io.github.jan.supabase.gotrue.Auth
-import io.github.jan.supabase.gotrue.authenticatedSupabaseApi
import io.github.jan.supabase.plugins.CustomSerializationConfig
import io.github.jan.supabase.plugins.CustomSerializationPlugin
import io.github.jan.supabase.plugins.MainConfig
@@ -152,6 +152,9 @@ class Functions(override val config: Config, override val supabaseClient: Supaba
}
+ /**
+ * @see Functions
+ */
companion object : SupabasePluginProvider {
override val key = "functions"
diff --git a/Functions/src/commonTest/kotlin/FunctionsTest.kt b/Functions/src/commonTest/kotlin/FunctionsTest.kt
index 43a2a12d2..50ffc5116 100644
--- a/Functions/src/commonTest/kotlin/FunctionsTest.kt
+++ b/Functions/src/commonTest/kotlin/FunctionsTest.kt
@@ -1,10 +1,10 @@
import io.github.jan.supabase.SupabaseClientBuilder
+import io.github.jan.supabase.auth.Auth
+import io.github.jan.supabase.auth.auth
+import io.github.jan.supabase.auth.minimalSettings
import io.github.jan.supabase.functions.FunctionRegion
import io.github.jan.supabase.functions.Functions
import io.github.jan.supabase.functions.functions
-import io.github.jan.supabase.gotrue.Auth
-import io.github.jan.supabase.gotrue.auth
-import io.github.jan.supabase.gotrue.minimalSettings
import io.github.jan.supabase.testing.createMockedSupabaseClient
import io.github.jan.supabase.testing.pathAfterVersion
import io.github.jan.supabase.testing.toJsonElement
diff --git a/GoTrue/README.md b/GoTrue/README.md
index 453611ac9..aa2fddbec 100644
--- a/GoTrue/README.md
+++ b/GoTrue/README.md
@@ -1,30 +1,40 @@
+# Deprecation notice
+
+**Starting with version 3.0.0, the module is called `auth-kt`. Checkout the updated [README](/Auth)**.
+
+The `gotrue-kt` artifact will no longer be published after version 3.0.0.
+
# Supabase-kt GoTrue
-Extends Supabase-kt with a multiplatform GoTrue client.
+Extends Supabase-kt with a multiplatform Auth client.
Supported targets:
-| Target | **JVM** | **Android** | **JS** | **iOS** | **tvOS** | **watchOS** | **macOS** | **Windows** | **Linux** |
-| ------ | ------- | ----------- | ------ | ------- | -------- | ----------- | --------- | ----------- | --------- |
-| | ✅ | ✅ | ✅ | ✅ | ☑️ | ☑️ | ✅ | ☑️ | ☑️ |
+| Target | **JVM** | **Android** | **JS** | **Wasm** | **Apple** | **Windows** | **Linux** |
+|--------|---------|-------------|--------|----------|-----------|-------------|-----------|
+| Status | ✅ | ✅ | ✅ | ✅ | ☑️* | ☑️ | ☑️ |
-> Native support is experimental and needs feedback
->
> ☑️ = No built-in OAuth support. Linux has no support for persistent session storage.
+\* **iOS and macOS are fully supported**
+
In-depth Kotlin targets
-**iOS:** iosArm64, iosSimulatorArm64, iosX64
-
**JS**: Browser, NodeJS
-**tvOS**: tvosArm64, tvosX64, tvosSimulatorArm64
+**Wasm**: wasm-js
-**watchOS**: watchosArm64, watchosX64, watchosSimulatorArm64
+**Apple:**
-**MacOS**: macosX64, macosArm64
+- iOS: iosArm64, iosSimulatorArm64, iosX64
+
+- tvOS: tvosArm64, tvosX64, tvosSimulatorArm64
+
+- watchOS: watchosArm64, watchosX64, watchosSimulatorArm64
+
+- MacOS: macosX64, macosArm64
**Windows**: mingwX64
@@ -36,6 +46,8 @@ Supported targets:
Newest version: [![](https://img.shields.io/github/release/supabase-community/supabase-kt?label=)](https://github.com/supabase-community/supabase-kt/releases)
+Versions above 3.0.0 are available under the `auth-kt` module.
+
```kotlin
dependencies {
implementation("io.github.jan-tennert.supabase:gotrue-kt:VERSION")
@@ -61,4 +73,4 @@ val supabase = createSupabaseClient(
# Usage
-See [GoTrue documentation](https://supabase.com/docs/reference/kotlin/auth-signup) for usage
+See [Auth documentation](https://supabase.com/docs/reference/kotlin/auth-signup) for usage
diff --git a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/SignOutScope.kt b/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/SignOutScope.kt
deleted file mode 100644
index d93adcea6..000000000
--- a/GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/SignOutScope.kt
+++ /dev/null
@@ -1,14 +0,0 @@
-package io.github.jan.supabase.gotrue
-
-/**
- * Represents the scope of a sign-out action.
- *
- * The sign-out scope determines the scope of the sign-out action being performed.
- *
- * @property GLOBAL Sign-out action applies to all sessions across the entire system.
- * @property LOCAL Sign-out action applies only to the current session.
- * @property OTHERS Sign-out action applies to other sessions, excluding the current session.
- */
-enum class SignOutScope {
- GLOBAL, LOCAL, OTHERS
-}
\ No newline at end of file
diff --git a/GoTrue/src/jsMain/kotlin/io/github/jan/supabase/gotrue/providers/ExternalAuthConfig.kt b/GoTrue/src/jsMain/kotlin/io/github/jan/supabase/gotrue/providers/ExternalAuthConfig.kt
deleted file mode 100644
index 59b620952..000000000
--- a/GoTrue/src/jsMain/kotlin/io/github/jan/supabase/gotrue/providers/ExternalAuthConfig.kt
+++ /dev/null
@@ -1,17 +0,0 @@
-package io.github.jan.supabase.gotrue.providers
-
-import kotlinx.browser.window
-
-/**
- * Configuration for external authentication providers like Google, Twitter, etc.
- */
-actual class ExternalAuthConfig: ExternalAuthConfigDefaults() {
-
- /**
- * The URL to redirect to after a successful login.
- *
- * Defaults to `Window.location.origin`
- */
- var redirectUrl: String = window.location.origin
-
-}
\ No newline at end of file
diff --git a/GoTrue/src/mingwTest/kotlin/platformSettings.kt b/GoTrue/src/mingwTest/kotlin/platformSettings.kt
deleted file mode 100644
index c97cb2bd9..000000000
--- a/GoTrue/src/mingwTest/kotlin/platformSettings.kt
+++ /dev/null
@@ -1,3 +0,0 @@
-import io.github.jan.supabase.gotrue.AuthConfig
-
-actual fun AuthConfig.platformSettings() = Unit
\ No newline at end of file
diff --git a/Postgrest/README.md b/Postgrest/README.md
index 94da8b919..c7c4f75a9 100644
--- a/Postgrest/README.md
+++ b/Postgrest/README.md
@@ -4,23 +4,27 @@ Extends Supabase-kt with a multiplatform Postgrest client.
Supported targets:
-| Target | **JVM** | **Android** | **JS** | **iOS** | **tvOS** | **watchOS** | **macOS** | **Windows** | **Linux** |
-| ------ | ------- | ----------- | ------ | ------- | -------- | ----------- | --------- | ----------- | --------- |
-| | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
+| Target | **JVM** | **Android** | **JS** | **Wasm** | **Apple** | **Windows** | **Linux** |
+|--------|---------|-------------|--------|----------|-----------|-------------|-----------|
+| Status | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
In-depth Kotlin targets
-**iOS:** iosArm64, iosSimulatorArm64, iosX64
-
**JS**: Browser, NodeJS
-**tvOS**: tvosArm64, tvosX64, tvosSimulatorArm64
+**Wasm**: wasm-js
+
+**Apple:**
+
+- iOS: iosArm64, iosSimulatorArm64, iosX64
+
+- tvOS: tvosArm64, tvosX64, tvosSimulatorArm64
-**watchOS**: watchosArm64, watchosX64, watchosSimulatorArm64
+- watchOS: watchosArm64, watchosX64, watchosSimulatorArm64
-**MacOS**: macosX64, macosArm64
+- MacOS: macosX64, macosArm64
**Windows**: mingwX64
diff --git a/Postgrest/build.gradle.kts b/Postgrest/build.gradle.kts
index 05fb3309d..bdc496cbe 100644
--- a/Postgrest/build.gradle.kts
+++ b/Postgrest/build.gradle.kts
@@ -16,13 +16,14 @@ kotlin {
sourceSets {
val commonMain by getting {
dependencies {
- addModules(SupabaseModule.GOTRUE)
+ addModules(SupabaseModule.AUTH)
api(libs.kotlin.reflect)
}
}
val commonTest by getting {
dependencies {
implementation(libs.bundles.testing)
+ implementation(project(":test-common"))
}
}
}
diff --git a/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/Postgrest.kt b/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/Postgrest.kt
index 15ff8d86d..9d163e831 100644
--- a/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/Postgrest.kt
+++ b/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/Postgrest.kt
@@ -2,6 +2,7 @@ package io.github.jan.supabase.postgrest
import io.github.jan.supabase.SupabaseClient
import io.github.jan.supabase.SupabaseSerializer
+import io.github.jan.supabase.exceptions.RestException
import io.github.jan.supabase.logging.SupabaseLogger
import io.github.jan.supabase.plugins.CustomSerializationConfig
import io.github.jan.supabase.plugins.CustomSerializationPlugin
@@ -11,6 +12,9 @@ import io.github.jan.supabase.plugins.SupabasePluginProvider
import io.github.jan.supabase.postgrest.query.PostgrestQueryBuilder
import io.github.jan.supabase.postgrest.query.PostgrestRequestBuilder
import io.github.jan.supabase.postgrest.query.PostgrestUpdate
+import io.github.jan.supabase.postgrest.query.request.RpcRequestBuilder
+import io.github.jan.supabase.postgrest.result.PostgrestResult
+import kotlinx.serialization.json.JsonObject
/**
* Plugin to interact with the supabase Postgrest API
@@ -53,11 +57,36 @@ sealed interface Postgrest : MainPlugin, CustomSerializationPl
/**
* Creates a new [PostgrestQueryBuilder] for the given schema and table
- * @param schema The schema to use for the requests
* @param table The table to use for the requests
*/
operator fun get(table: String): PostgrestQueryBuilder = from(table)
+ /**
+ * Executes a database function
+ *
+ * @param function The name of the function
+ * @param request Filter the result
+ * @throws RestException or one of its subclasses if the request failed
+ */
+ suspend fun rpc(
+ function: String,
+ request: RpcRequestBuilder.() -> Unit = {}
+ ): PostgrestResult
+
+ /**
+ * Executes a database function
+ *
+ * @param function The name of the function
+ * @param parameters The parameters for the function
+ * @param request Filter the result
+ * @throws RestException or one of its subclasses if the request failed
+ */
+ suspend fun rpc(
+ function: String,
+ parameters: JsonObject,
+ request: RpcRequestBuilder.() -> Unit = {},
+ ): PostgrestResult
+
/**
* Config for the Postgrest plugin
* @param defaultSchema The default schema to use for the requests. Defaults to "public"
diff --git a/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/PostgrestImpl.kt b/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/PostgrestImpl.kt
index 86c37b69b..f4e806326 100644
--- a/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/PostgrestImpl.kt
+++ b/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/PostgrestImpl.kt
@@ -2,16 +2,21 @@ package io.github.jan.supabase.postgrest
import io.github.jan.supabase.SupabaseClient
import io.github.jan.supabase.annotations.SupabaseInternal
+import io.github.jan.supabase.auth.authenticatedSupabaseApi
import io.github.jan.supabase.bodyOrNull
import io.github.jan.supabase.exceptions.BadRequestRestException
import io.github.jan.supabase.exceptions.NotFoundRestException
import io.github.jan.supabase.exceptions.RestException
import io.github.jan.supabase.exceptions.UnauthorizedRestException
import io.github.jan.supabase.exceptions.UnknownRestException
-import io.github.jan.supabase.gotrue.authenticatedSupabaseApi
+import io.github.jan.supabase.postgrest.executor.RestRequestExecutor
import io.github.jan.supabase.postgrest.query.PostgrestQueryBuilder
+import io.github.jan.supabase.postgrest.query.request.RpcRequestBuilder
+import io.github.jan.supabase.postgrest.request.RpcRequest
+import io.github.jan.supabase.postgrest.result.PostgrestResult
import io.ktor.client.statement.HttpResponse
import io.ktor.http.HttpStatusCode
+import kotlinx.serialization.json.JsonObject
internal class PostgrestImpl(override val supabaseClient: SupabaseClient, override val config: Postgrest.Config) : Postgrest {
@@ -51,4 +56,31 @@ internal class PostgrestImpl(override val supabaseClient: SupabaseClient, overri
}
}
+ override suspend fun rpc(
+ function: String,
+ parameters: JsonObject,
+ request: RpcRequestBuilder.() -> Unit
+ ): PostgrestResult = rpcRequest(function, parameters, request)
+
+ override suspend fun rpc(function: String, request: RpcRequestBuilder.() -> Unit): PostgrestResult = rpcRequest(function, null, request)
+
+ private suspend fun rpcRequest(function: String, body: JsonObject? = null, request: RpcRequestBuilder.() -> Unit): PostgrestResult {
+ val requestBuilder = RpcRequestBuilder(config.defaultSchema, config.propertyConversionMethod).apply(request)
+ val urlParams = buildMap {
+ putAll(requestBuilder.params.mapToFirstValue())
+ if(requestBuilder.method != RpcMethod.POST && body != null) {
+ putAll(body.mapValues { it.value.toString() })
+ }
+ }
+ val rpcRequest = RpcRequest(
+ method = requestBuilder.method.httpMethod,
+ count = requestBuilder.count,
+ urlParams = urlParams,
+ body = body,
+ schema = requestBuilder.schema,
+ headers = requestBuilder.headers.build()
+ )
+ return RestRequestExecutor.execute(postgrest = this, path = "rpc/$function", request = rpcRequest)
+ }
+
}
\ No newline at end of file
diff --git a/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/PostgrestRpc.kt b/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/PostgrestRpc.kt
index d98cb5265..0ee05f226 100644
--- a/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/PostgrestRpc.kt
+++ b/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/PostgrestRpc.kt
@@ -2,12 +2,10 @@
package io.github.jan.supabase.postgrest
import io.github.jan.supabase.encodeToJsonElement
-import io.github.jan.supabase.postgrest.executor.RestRequestExecutor
-import io.github.jan.supabase.postgrest.query.PostgrestRequestBuilder
-import io.github.jan.supabase.postgrest.request.RpcRequest
+import io.github.jan.supabase.exceptions.RestException
+import io.github.jan.supabase.postgrest.query.request.RpcRequestBuilder
import io.github.jan.supabase.postgrest.result.PostgrestResult
import io.ktor.http.HttpMethod
-import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.jsonObject
/**
@@ -34,53 +32,11 @@ enum class RpcMethod(val httpMethod: HttpMethod) {
*
* @param function The name of the function
* @param parameters The parameters for the function
- * @param method The HTTP method to use. Default is POST
* @param request Filter the result
* @throws RestException or one of its subclasses if the request failed
*/
suspend inline fun Postgrest.rpc(
function: String,
parameters: T,
- method: RpcMethod = RpcMethod.POST,
- request: PostgrestRequestBuilder.() -> Unit = {},
-): PostgrestResult {
- val encodedParameters = if (parameters is JsonElement) parameters else serializer.encodeToJsonElement(parameters)
- val requestBuilder = PostgrestRequestBuilder(config.propertyConversionMethod).apply(request)
- val urlParams = buildMap {
- putAll(requestBuilder.params.mapToFirstValue())
- if(method != RpcMethod.POST) {
- putAll(encodedParameters.jsonObject.mapValues { it.value.toString() })
- }
- }
- val rpcRequest = RpcRequest(
- method = method.httpMethod,
- count = requestBuilder.count,
- urlParams = urlParams,
- body = encodedParameters,
- headers = requestBuilder.headers.build()
- )
- return RestRequestExecutor.execute(postgrest = this, path = "rpc/$function", request = rpcRequest)
-}
-
-/**
- * Executes a database function
- *
- * @param function The name of the function
- * @param method The HTTP method to use. Default is POST
- * @param request Filter the result
- * @throws RestException or one of its subclasses if the request failed
- */
-suspend inline fun Postgrest.rpc(
- function: String,
- method: RpcMethod = RpcMethod.POST,
- request: PostgrestRequestBuilder.() -> Unit = {}
-): PostgrestResult {
- val requestBuilder = PostgrestRequestBuilder(config.propertyConversionMethod).apply(request)
- val rpcRequest = RpcRequest(
- method = method.httpMethod,
- count = requestBuilder.count,
- urlParams = requestBuilder.params.mapToFirstValue(),
- headers = requestBuilder.headers.build()
- )
- return RestRequestExecutor.execute(postgrest = this, path = "rpc/$function", request = rpcRequest)
-}
+ noinline request: RpcRequestBuilder.() -> Unit = {},
+): PostgrestResult = rpc(function, serializer.encodeToJsonElement(parameters).jsonObject, request)
\ No newline at end of file
diff --git a/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/query/Order.kt b/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/query/Order.kt
index e3c5b1fda..2ac8b6970 100644
--- a/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/query/Order.kt
+++ b/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/query/Order.kt
@@ -5,5 +5,13 @@ package io.github.jan.supabase.postgrest.query
* @param value The value to be used in the query
*/
enum class Order(val value: String) {
- ASCENDING("asc"), DESCENDING("desc");
+ /**
+ * Order the data ascending
+ */
+ ASCENDING("asc"),
+
+ /**
+ * Order the data descending
+ */
+ DESCENDING("desc");
}
diff --git a/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/query/PostgrestQueryBuilder.kt b/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/query/PostgrestQueryBuilder.kt
index 80e55ef92..7b06faa30 100644
--- a/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/query/PostgrestQueryBuilder.kt
+++ b/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/query/PostgrestQueryBuilder.kt
@@ -1,13 +1,16 @@
@file:Suppress("UndocumentedPublicProperty")
package io.github.jan.supabase.postgrest.query
+import io.github.jan.supabase.auth.PostgrestFilterDSL
import io.github.jan.supabase.encodeToJsonElement
import io.github.jan.supabase.exceptions.HttpRequestException
import io.github.jan.supabase.exceptions.RestException
-import io.github.jan.supabase.gotrue.PostgrestFilterDSL
import io.github.jan.supabase.postgrest.Postgrest
import io.github.jan.supabase.postgrest.executor.RestRequestExecutor
import io.github.jan.supabase.postgrest.mapToFirstValue
+import io.github.jan.supabase.postgrest.query.request.InsertRequestBuilder
+import io.github.jan.supabase.postgrest.query.request.SelectRequestBuilder
+import io.github.jan.supabase.postgrest.query.request.UpsertRequestBuilder
import io.github.jan.supabase.postgrest.request.DeleteRequest
import io.github.jan.supabase.postgrest.request.InsertRequest
import io.github.jan.supabase.postgrest.request.SelectRequest
@@ -30,8 +33,7 @@ class PostgrestQueryBuilder(
* Executes vertical filtering with select on [table]
*
* @param columns The columns to retrieve, defaults to [Columns.ALL]. You can also use [Columns.list], [Columns.type] or [Columns.raw] to specify the columns
- * @param head If true, no body will be returned. Useful when using count.
- * @param request Additional filtering to apply to the query
+ * @param request Additional configurations for the request including filters
* @return PostgrestResult which is either an error, an empty JsonArray or the data you requested as an JsonArray
* @throws RestException or one of its subclasses if receiving an error response
* @throws HttpRequestTimeoutException if the request timed out
@@ -39,14 +41,13 @@ class PostgrestQueryBuilder(
*/
suspend inline fun select(
columns: Columns = Columns.ALL,
- head: Boolean = false,
- request: @PostgrestFilterDSL PostgrestRequestBuilder.() -> Unit = {}
+ request: @PostgrestFilterDSL SelectRequestBuilder.() -> Unit = {}
): PostgrestResult {
- val requestBuilder = postgrestRequest(postgrest.config.propertyConversionMethod) {
+ val requestBuilder = SelectRequestBuilder(postgrest.config.propertyConversionMethod).apply {
request(); params["select"] = listOf(columns.value)
}
val selectRequest = SelectRequest(
- head = head,
+ head = requestBuilder.head,
count = requestBuilder.count,
urlParams = requestBuilder.params.mapToFirstValue(),
schema = schema,
@@ -57,38 +58,28 @@ class PostgrestQueryBuilder(
/**
* Perform an UPSERT on the table or view. Depending on the column(s) passed
- * to [onConflict], [upsert] allows you to perform the equivalent of
+ * to [UpsertRequestBuilder.onConflict], [upsert] allows you to perform the equivalent of
* `[insert] if a row with the corresponding onConflict columns doesn't
* exist, or if it does exist, perform an alternative action depending on
- * [ignoreDuplicates].
+ * [UpsertRequestBuilder.ignoreDuplicates].
*
* By default, upserted rows are not returned. To return it, call `[PostgrestRequestBuilder.select]`.
*
* @param values The values to insert, will automatically get serialized into json.
- * @param request Additional filtering to apply to the query
- * @param onConflict Comma-separated UNIQUE column(s) to specify how
- * duplicate rows are determined. Two rows are duplicates if all the
- * `onConflict` columns are equal.
- * @param defaultToNull Make missing fields default to `null`.
- * Otherwise, use the default value for the column. This only applies when
- * inserting new rows, not when merging with existing rows under
- * @param ignoreDuplicates If `true`, duplicate rows are ignored. If `false`, duplicate rows are merged with existing rows.
+ * @param request Additional configurations for the request including filters
* @throws RestException or one of its subclasses if receiving an error response
* @throws HttpRequestTimeoutException if the request timed out
* @throws HttpRequestException on network related issues
*/
suspend inline fun upsert(
values: List,
- onConflict: String? = null,
- defaultToNull: Boolean = true,
- ignoreDuplicates: Boolean = false,
- request: PostgrestRequestBuilder.() -> Unit = {}
+ request: UpsertRequestBuilder.() -> Unit = {}
): PostgrestResult {
- val requestBuilder = postgrestRequest(postgrest.config.propertyConversionMethod, request)
+ val requestBuilder = UpsertRequestBuilder(postgrest.config.propertyConversionMethod).apply(request)
val body = postgrest.serializer.encodeToJsonElement(values).jsonArray
val columns = body.map { it.jsonObject.keys }.flatten().distinct()
requestBuilder.params["columns"] = listOf(columns.joinToString(","))
- onConflict?.let {
+ requestBuilder.onConflict?.let {
requestBuilder.params["on_conflict"] = listOf(it)
}
val insertRequest = InsertRequest(
@@ -97,8 +88,8 @@ class PostgrestQueryBuilder(
returning = requestBuilder.returning,
count = requestBuilder.count,
urlParams = requestBuilder.params.mapToFirstValue(),
- defaultToNull = defaultToNull,
- ignoreDuplicates = ignoreDuplicates,
+ defaultToNull = requestBuilder.defaultToNull,
+ ignoreDuplicates = requestBuilder.ignoreDuplicates,
schema = schema,
headers = requestBuilder.headers.build()
)
@@ -107,52 +98,38 @@ class PostgrestQueryBuilder(
/**
* Perform an UPSERT on the table or view. Depending on the column(s) passed
- * to [onConflict], [upsert] allows you to perform the equivalent of
+ * to [UpsertRequestBuilder.onConflict], [upsert] allows you to perform the equivalent of
* `[insert] if a row with the corresponding onConflict columns doesn't
* exist, or if it does exist, perform an alternative action depending on
- * [ignoreDuplicates].
+ * [UpsertRequestBuilder.ignoreDuplicates].
*
* By default, upserted rows are not returned. To return it, call `[PostgrestRequestBuilder.select]`.
*
* @param value The value to insert, will automatically get serialized into json.
* @param request Additional filtering to apply to the query
- * @param onConflict Comma-separated UNIQUE column(s) to specify how
- * duplicate rows are determined. Two rows are duplicates if all the
- * `onConflict` columns are equal.
- * @param defaultToNull Make missing fields default to `null`.
- * Otherwise, use the default value for the column. This only applies when
- * inserting new rows, not when merging with existing rows under
- * @param ignoreDuplicates If `true`, duplicate rows are ignored. If `false`, duplicate rows are merged with existing rows.
* @throws RestException or one of its subclasses if receiving an error response
* @throws HttpRequestTimeoutException if the request timed out
* @throws HttpRequestException on network related issues
*/
suspend inline fun upsert(
value: T,
- onConflict: String? = null,
- defaultToNull: Boolean = true,
- ignoreDuplicates: Boolean = false,
- request: PostgrestRequestBuilder.() -> Unit = {}
- ): PostgrestResult = upsert(listOf(value), onConflict, defaultToNull, ignoreDuplicates, request)
+ request: UpsertRequestBuilder.() -> Unit = {}
+ ): PostgrestResult = upsert(listOf(value), request)
/**
* Executes an insert operation on the [table]
*
* @param values The values to insert, will automatically get serialized into json.
* @param request Additional filtering to apply to the query
- * @param defaultToNull Make missing fields default to `null`.
- * Otherwise, use the default value for the column. This only applies when
- * inserting new rows, not when merging with existing rows under
* @throws RestException or one of its subclasses if receiving an error response
* @throws HttpRequestTimeoutException if the request timed out
* @throws HttpRequestException on network related issues
*/
suspend inline fun insert(
values: List,
- defaultToNull: Boolean = true,
- request: PostgrestRequestBuilder.() -> Unit = {}
+ request: InsertRequestBuilder.() -> Unit = {}
): PostgrestResult {
- val requestBuilder = postgrestRequest(postgrest.config.propertyConversionMethod, request)
+ val requestBuilder = InsertRequestBuilder(postgrest.config.propertyConversionMethod).apply(request)
val body = postgrest.serializer.encodeToJsonElement(values).jsonArray
val columns = body.map { it.jsonObject.keys }.flatten().distinct()
requestBuilder.params["columns"] = listOf(columns.joinToString(","))
@@ -163,7 +140,7 @@ class PostgrestQueryBuilder(
urlParams = requestBuilder.params.mapToFirstValue(),
schema = schema,
headers = requestBuilder.headers.build(),
- defaultToNull = defaultToNull
+ defaultToNull = requestBuilder.defaultToNull
)
return RestRequestExecutor.execute(postgrest = postgrest, path = table, request = insertRequest)
}
@@ -173,18 +150,14 @@ class PostgrestQueryBuilder(
*
* @param value The value to insert, will automatically get serialized into json.
* @param request Additional filtering to apply to the query
- * @param defaultToNull Make missing fields default to `null`.
- * Otherwise, use the default value for the column. This only applies when
- * inserting new rows, not when merging with existing rows under
* @throws RestException or one of its subclasses if receiving an error response
* @throws HttpRequestTimeoutException if the request timed out
* @throws HttpRequestException on network related issues
*/
suspend inline fun insert(
value: T,
- defaultToNull: Boolean = true,
- request: PostgrestRequestBuilder.() -> Unit = {}
- ) = insert(listOf(value), defaultToNull, request)
+ request: InsertRequestBuilder.() -> Unit = {}
+ ) = insert(listOf(value), request)
/**
* Executes an update operation on the [table].
@@ -201,7 +174,7 @@ class PostgrestQueryBuilder(
crossinline update: PostgrestUpdate.() -> Unit = {},
request: PostgrestRequestBuilder.() -> Unit = {}
): PostgrestResult {
- val requestBuilder = postgrestRequest(postgrest.config.propertyConversionMethod, request)
+ val requestBuilder = PostgrestRequestBuilder(postgrest.config.propertyConversionMethod).apply(request)
val updateRequest = UpdateRequest(
body = buildPostgrestUpdate(postgrest.config.propertyConversionMethod, postgrest.serializer, update),
returning = requestBuilder.returning,
@@ -228,7 +201,7 @@ class PostgrestQueryBuilder(
value: T,
request: PostgrestRequestBuilder.() -> Unit = {}
): PostgrestResult {
- val requestBuilder = postgrestRequest(postgrest.config.propertyConversionMethod, request)
+ val requestBuilder = PostgrestRequestBuilder(postgrest.config.propertyConversionMethod).apply(request)
val updateRequest = UpdateRequest(
returning = requestBuilder.returning,
count = requestBuilder.count,
@@ -253,7 +226,7 @@ class PostgrestQueryBuilder(
suspend inline fun delete(
request: PostgrestRequestBuilder.() -> Unit = {}
): PostgrestResult {
- val requestBuilder = postgrestRequest(postgrest.config.propertyConversionMethod, request)
+ val requestBuilder = PostgrestRequestBuilder(postgrest.config.propertyConversionMethod).apply(request)
val deleteRequest = DeleteRequest(
returning = requestBuilder.returning,
count = requestBuilder.count,
diff --git a/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/query/PostgrestRequestBuilder.kt b/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/query/PostgrestRequestBuilder.kt
index bb48d6bc0..656d016cb 100644
--- a/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/query/PostgrestRequestBuilder.kt
+++ b/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/query/PostgrestRequestBuilder.kt
@@ -2,8 +2,7 @@
package io.github.jan.supabase.postgrest.query
import io.github.jan.supabase.annotations.SupabaseExperimental
-import io.github.jan.supabase.annotations.SupabaseInternal
-import io.github.jan.supabase.gotrue.PostgrestFilterDSL
+import io.github.jan.supabase.auth.PostgrestFilterDSL
import io.github.jan.supabase.postgrest.PropertyConversionMethod
import io.github.jan.supabase.postgrest.query.filter.PostgrestFilterBuilder
import io.github.jan.supabase.postgrest.result.PostgrestResult
@@ -15,7 +14,7 @@ import kotlin.js.JsName
* A builder for Postgrest requests.
*/
@PostgrestFilterDSL
-class PostgrestRequestBuilder(@PublishedApi internal val propertyConversionMethod: PropertyConversionMethod) {
+open class PostgrestRequestBuilder(@PublishedApi internal val propertyConversionMethod: PropertyConversionMethod) {
/**
* The [Count] algorithm to use to count rows in the table or view.
@@ -132,6 +131,7 @@ class PostgrestRequestBuilder(@PublishedApi internal val propertyConversionMetho
* @param format - The format of the output, can be `"text"` (default)
* or `"json"`
*/
+ @Suppress("LongParameterList")
fun explain(
analyze: Boolean = false,
verbose: Boolean = false,
@@ -162,11 +162,3 @@ class PostgrestRequestBuilder(@PublishedApi internal val propertyConversionMetho
}
-@SupabaseInternal
-inline fun postgrestRequest(propertyConversionMethod: PropertyConversionMethod = PropertyConversionMethod.CAMEL_CASE_TO_SNAKE_CASE, block: PostgrestRequestBuilder.() -> Unit): PostgrestRequestBuilder {
- val filter = PostgrestRequestBuilder(propertyConversionMethod)
- filter.block()
- return filter
-}
-
-
diff --git a/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/query/filter/PostgrestFilterBuilder.kt b/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/query/filter/PostgrestFilterBuilder.kt
index 108ad8af6..0021ce0ff 100644
--- a/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/query/filter/PostgrestFilterBuilder.kt
+++ b/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/query/filter/PostgrestFilterBuilder.kt
@@ -1,7 +1,7 @@
@file:Suppress("UndocumentedPublicProperty", "ConstructorParameterNaming")
package io.github.jan.supabase.postgrest.query.filter
-import io.github.jan.supabase.gotrue.PostgrestFilterDSL
+import io.github.jan.supabase.auth.PostgrestFilterDSL
import io.github.jan.supabase.postgrest.PropertyConversionMethod
import kotlin.reflect.KProperty1
diff --git a/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/query/request/InsertRequestBuilder.kt b/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/query/request/InsertRequestBuilder.kt
new file mode 100644
index 000000000..4c3f5ff28
--- /dev/null
+++ b/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/query/request/InsertRequestBuilder.kt
@@ -0,0 +1,19 @@
+package io.github.jan.supabase.postgrest.query.request
+
+import io.github.jan.supabase.postgrest.PropertyConversionMethod
+import io.github.jan.supabase.postgrest.query.PostgrestQueryBuilder
+import io.github.jan.supabase.postgrest.query.PostgrestRequestBuilder
+
+/**
+ * Request builder for [PostgrestQueryBuilder.insert]
+ */
+open class InsertRequestBuilder(propertyConversionMethod: PropertyConversionMethod): PostgrestRequestBuilder(propertyConversionMethod) {
+
+ /**
+ * Make missing fields default to `null`.
+ * Otherwise, use the default value for the column. This only applies when
+ * inserting new rows, not when merging with existing rows under
+ */
+ var defaultToNull: Boolean = true
+
+}
\ No newline at end of file
diff --git a/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/query/request/RpcRequestBuilder.kt b/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/query/request/RpcRequestBuilder.kt
new file mode 100644
index 000000000..8d7b1fe68
--- /dev/null
+++ b/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/query/request/RpcRequestBuilder.kt
@@ -0,0 +1,22 @@
+package io.github.jan.supabase.postgrest.query.request
+
+import io.github.jan.supabase.postgrest.PropertyConversionMethod
+import io.github.jan.supabase.postgrest.RpcMethod
+import io.github.jan.supabase.postgrest.query.PostgrestRequestBuilder
+
+/**
+ * Request builder for [Postgrest.rpcRequest]
+ */
+class RpcRequestBuilder(defaultSchema: String, propertyConversionMethod: PropertyConversionMethod): PostgrestRequestBuilder(propertyConversionMethod) {
+
+ /**
+ * The HTTP method to use. Default is POST
+ */
+ var method: RpcMethod = RpcMethod.POST
+
+ /**
+ * The database schema
+ */
+ var schema: String = defaultSchema
+
+}
\ No newline at end of file
diff --git a/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/query/request/SelectRequestBuilder.kt b/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/query/request/SelectRequestBuilder.kt
new file mode 100644
index 000000000..1ac90fac3
--- /dev/null
+++ b/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/query/request/SelectRequestBuilder.kt
@@ -0,0 +1,17 @@
+package io.github.jan.supabase.postgrest.query.request
+
+import io.github.jan.supabase.postgrest.PropertyConversionMethod
+import io.github.jan.supabase.postgrest.query.PostgrestQueryBuilder
+import io.github.jan.supabase.postgrest.query.PostgrestRequestBuilder
+
+/**
+ * Request builder for [PostgrestQueryBuilder.select]
+ */
+class SelectRequestBuilder(propertyConversionMethod: PropertyConversionMethod): PostgrestRequestBuilder(propertyConversionMethod) {
+
+ /**
+ * If true, no body will be returned. Useful when using count.
+ */
+ var head: Boolean = false
+
+}
\ No newline at end of file
diff --git a/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/query/request/UpsertRequestBuilder.kt b/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/query/request/UpsertRequestBuilder.kt
new file mode 100644
index 000000000..024e00338
--- /dev/null
+++ b/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/query/request/UpsertRequestBuilder.kt
@@ -0,0 +1,23 @@
+package io.github.jan.supabase.postgrest.query.request
+
+import io.github.jan.supabase.postgrest.PropertyConversionMethod
+import io.github.jan.supabase.postgrest.query.PostgrestQueryBuilder
+
+/**
+ * Request builder for [PostgrestQueryBuilder.upsert]
+ */
+class UpsertRequestBuilder(propertyConversionMethod: PropertyConversionMethod): InsertRequestBuilder(propertyConversionMethod) {
+
+ /**
+ * Comma-separated UNIQUE column(s) to specify how
+ * duplicate rows are determined. Two rows are duplicates if all the
+ * `onConflict` columns are equal.
+ */
+ var onConflict: String? = null
+
+ /**
+ * If `true`, duplicate rows are ignored. If `false`, duplicate rows are merged with existing rows.
+ */
+ var ignoreDuplicates: Boolean = false
+
+}
\ No newline at end of file
diff --git a/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/request/RpcRequest.kt b/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/request/RpcRequest.kt
index 4fe29b438..fdd9a3094 100644
--- a/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/request/RpcRequest.kt
+++ b/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/request/RpcRequest.kt
@@ -1,9 +1,9 @@
package io.github.jan.supabase.postgrest.request
import io.github.jan.supabase.postgrest.query.Count
+import io.ktor.http.Headers
import io.ktor.http.HttpMethod
import kotlinx.serialization.json.JsonElement
-import io.ktor.http.Headers
@PublishedApi
internal class RpcRequest(
@@ -11,10 +11,10 @@ internal class RpcRequest(
val count: Count? = null,
override val urlParams: Map,
override val body: JsonElement? = null,
+ override val schema: String = "public",
override val headers: Headers = Headers.Empty
) : PostgrestRequest {
- override val schema: String = ""
override val prefer = if (count != null) listOf("count=${count.identifier}") else listOf()
-}
+}
\ No newline at end of file
diff --git a/Postgrest/src/commonTest/kotlin/PostgrestRequestBuilderTest.kt b/Postgrest/src/commonTest/kotlin/PostgrestRequestBuilderTest.kt
index b84e13548..1ddc65858 100644
--- a/Postgrest/src/commonTest/kotlin/PostgrestRequestBuilderTest.kt
+++ b/Postgrest/src/commonTest/kotlin/PostgrestRequestBuilderTest.kt
@@ -1,7 +1,6 @@
import io.github.jan.supabase.postgrest.query.Count
import io.github.jan.supabase.postgrest.query.Order
import io.github.jan.supabase.postgrest.query.Returning
-import io.github.jan.supabase.postgrest.query.postgrestRequest
import io.ktor.http.HttpHeaders
import kotlin.test.Test
import kotlin.test.assertEquals
@@ -129,4 +128,4 @@ class PostgrestRequestBuilderTest {
assertEquals("application/vnd.pgrst.plan+json; for=\"application/json\"; options=analyze|verbose|settings|buffers|wal;", request.headers[HttpHeaders.Accept])
}
-}
\ No newline at end of file
+}
diff --git a/Postgrest/src/commonTest/kotlin/PostgrestTest.kt b/Postgrest/src/commonTest/kotlin/PostgrestTest.kt
new file mode 100644
index 000000000..41827cd8f
--- /dev/null
+++ b/Postgrest/src/commonTest/kotlin/PostgrestTest.kt
@@ -0,0 +1,343 @@
+import io.github.jan.supabase.SupabaseClient
+import io.github.jan.supabase.SupabaseClientBuilder
+import io.github.jan.supabase.postgrest.Postgrest
+import io.github.jan.supabase.postgrest.RpcMethod
+import io.github.jan.supabase.postgrest.from
+import io.github.jan.supabase.postgrest.postgrest
+import io.github.jan.supabase.postgrest.query.Columns
+import io.github.jan.supabase.postgrest.query.request.InsertRequestBuilder
+import io.github.jan.supabase.postgrest.query.request.UpsertRequestBuilder
+import io.github.jan.supabase.postgrest.result.PostgrestResult
+import io.github.jan.supabase.testing.assertMethodIs
+import io.github.jan.supabase.testing.assertPathIs
+import io.github.jan.supabase.testing.createMockedSupabaseClient
+import io.github.jan.supabase.testing.pathAfterVersion
+import io.github.jan.supabase.testing.toJsonElement
+import io.ktor.client.engine.mock.MockRequestHandleScope
+import io.ktor.client.engine.mock.respond
+import io.ktor.client.request.HttpRequestData
+import io.ktor.client.request.HttpResponseData
+import io.ktor.http.HttpMethod
+import kotlinx.coroutines.test.runTest
+import kotlinx.serialization.json.buildJsonArray
+import kotlinx.serialization.json.buildJsonObject
+import kotlinx.serialization.json.jsonArray
+import kotlinx.serialization.json.put
+import kotlin.test.Test
+import kotlin.test.assertContains
+import kotlin.test.assertEquals
+
+class PostgrestTest {
+
+ private val configureClient: SupabaseClientBuilder.() -> Unit = {
+ install(Postgrest)
+ }
+
+ @Test
+ fun testSelectHttpMethodGet() {
+ val columns = Columns.list("column1", "column2")
+ testClient(
+ request = { table ->
+ from(table).select(columns) {
+ headers["custom"] = "value"
+ params["custom"] = listOf("value")
+ }
+ },
+ requestHandler = {
+ assertMethodIs(HttpMethod.Get, it.method)
+ assertEquals(columns.value, it.url.parameters["select"])
+ assertEquals("value", it.headers["custom"])
+ assertEquals("value", it.url.parameters["custom"])
+ respond("")
+ }
+ )
+ }
+
+ @Test
+ fun testSelectSchema() {
+ val columns = Columns.list("column1", "column2")
+ testClient(
+ request = { table ->
+ from("schema", table).select(columns)
+ },
+ requestHandler = {
+ assertEquals("schema", it.headers["Accept-Profile"])
+ assertMethodIs(HttpMethod.Get, it.method)
+ assertEquals(columns.value, it.url.parameters["select"])
+ respond("")
+ }
+ )
+ }
+
+ @Test
+ fun testSelectHttpMethodHead() {
+ val columns = Columns.list("column1", "column2")
+ testClient(
+ request = { table ->
+ from(table).select(columns) {
+ head = true
+ }
+ },
+ requestHandler = {
+ assertMethodIs(HttpMethod.Head, it.method)
+ assertEquals(columns.value, it.url.parameters["select"])
+ respond("")
+ }
+ )
+ }
+
+ @Test
+ fun testInsert() {
+ insertTestClient(
+ request = {
+ },
+ requestHandler = {
+ val prefer = it.headers["Prefer"]?.split(",") ?: emptyList()
+ assertContains(prefer, "return=minimal") //default
+ }
+ )
+ }
+
+ @Test
+ fun testInsertDefaultToNull() {
+ insertTestClient(
+ request = {
+ defaultToNull = false
+ },
+ requestHandler = {
+ val prefer = it.headers["Prefer"]?.split(",") ?: emptyList()
+ assertContains(prefer, "return=minimal") //default
+ assertContains(prefer, "missing=default")
+ }
+ )
+ }
+
+ @Test
+ fun testInsertWithSelect() {
+ insertTestClient(
+ request = {
+ select(Columns.raw("column1,column2"))
+ },
+ requestHandler = {
+ val prefer = it.headers["Prefer"]?.split(",") ?: emptyList()
+ assertEquals("column1,column2", it.url.parameters["select"])
+ assertContains(prefer, "return=representation")
+ }
+ )
+ }
+
+ private fun insertTestClient(
+ request: InsertRequestBuilder.() -> Unit,
+ requestHandler: suspend MockRequestHandleScope.(HttpRequestData) -> Unit,
+ ) {
+ val mockData = buildJsonObject {
+ put("column1", "value1")
+ put("column2", "value2")
+ }
+ testClient(
+ request = { table ->
+ from("schema", table).insert(mockData) {
+ request()
+ headers["custom"] = "value"
+ params["custom"] = listOf("value")
+ }
+ },
+ requestHandler = {
+ assertEquals(mockData, it.body.toJsonElement().jsonArray.first())
+ requestHandler(it)
+ assertMethodIs(HttpMethod.Post, it.method)
+ assertEquals("value", it.headers["custom"])
+ assertEquals("value", it.url.parameters["custom"])
+ assertEquals("schema", it.headers["Content-Profile"])
+ respond("")
+ }
+ )
+ }
+
+ @Test
+ fun testUpsert() {
+ upsertTestClient(
+ request = {
+ },
+ requestHandler = {
+ val prefer = it.headers["Prefer"]?.split(",") ?: emptyList()
+ assertContains(prefer, "resolution=merge-duplicates") //default
+ }
+ )
+ }
+
+ @Test
+ fun testUpsertIgnoreDuplicates() {
+ upsertTestClient(
+ request = {
+ ignoreDuplicates = true
+ },
+ requestHandler = {
+ val prefer = it.headers["Prefer"]?.split(",") ?: emptyList()
+ assertContains(prefer, "resolution=ignore-duplicates")
+ }
+ )
+ }
+
+ @Test
+ fun testUpsertWithSelect() {
+ upsertTestClient(
+ request = {
+ select(Columns.raw("column1,column2"))
+ },
+ requestHandler = {
+ val prefer = it.headers["Prefer"]?.split(",") ?: emptyList()
+ assertEquals("column1,column2", it.url.parameters["select"])
+ assertContains(prefer, "return=representation")
+ }
+ )
+ }
+
+ @Test
+ fun testUpsertWithDefaultToNull() {
+ upsertTestClient(
+ request = {
+ defaultToNull = false
+ },
+ requestHandler = {
+ val prefer = it.headers["Prefer"]?.split(",") ?: emptyList()
+ assertContains(prefer, "missing=default")
+ }
+ )
+ }
+
+ @Test
+ fun testUpsertWithOnConflict() {
+ upsertTestClient(
+ request = {
+ onConflict = "column1"
+ },
+ requestHandler = {
+ assertEquals("column1", it.url.parameters["on_conflict"])
+ }
+ )
+ }
+
+ private fun upsertTestClient(
+ request: UpsertRequestBuilder.() -> Unit,
+ requestHandler: suspend MockRequestHandleScope.(HttpRequestData) -> Unit,
+ ) {
+ val mockData = buildJsonArray {
+ add(buildJsonObject {
+ put("column1", "value1")
+ put("column2", "value2")
+ })
+ add(buildJsonObject {
+ put("column1", "value1")
+ put("column3", "value3")
+ })
+ }
+ testClient(
+ request = { table ->
+ from("schema", table).upsert(mockData) {
+ request()
+ headers["custom"] = "value"
+ params["custom"] = listOf("value")
+ }
+ },
+ requestHandler = {
+ assertEquals(mockData, it.body.toJsonElement())
+ requestHandler(it)
+ assertMethodIs(HttpMethod.Post, it.method)
+ assertEquals("column1,column2,column3", it.url.parameters["columns"])
+ assertEquals("value", it.headers["custom"])
+ assertEquals("schema", it.headers["Content-Profile"])
+ assertEquals("value", it.url.parameters["custom"])
+ respond("")
+ }
+ )
+ }
+
+ @Test
+ fun testUpdate() {
+ val columns = Columns.list("column1", "column2")
+ val mockData = buildJsonObject {
+ put("column1", "value1")
+ put("column2", "value2")
+ }
+ testClient(
+ request = { table ->
+ from(table).update(mockData) {
+ select(columns)
+ headers["custom"] = "value"
+ params["custom"] = listOf("value")
+ }
+ },
+ requestHandler = {
+ assertMethodIs(HttpMethod.Patch, it.method)
+ assertEquals(mockData, it.body.toJsonElement())
+ val prefer = it.headers["Prefer"]?.split(",") ?: emptyList()
+ assertEquals("column1,column2", it.url.parameters["select"])
+ assertContains(prefer, "return=representation")
+ assertEquals("value", it.headers["custom"])
+ assertEquals("value", it.url.parameters["custom"])
+ respond("")
+ }
+ )
+ }
+
+ @Test
+ fun testRpcNoParameters() {
+ val supabase = createMockedSupabaseClient(
+ configuration = configureClient
+ ) {
+ assertPathIs("/rpc/function", it.url.pathAfterVersion())
+ assertMethodIs(HttpMethod.Head, it.method)
+ assertEquals("schema", it.headers["Accept-Profile"])
+ assertEquals("value", it.headers["custom"])
+ assertEquals("value", it.url.parameters["custom"])
+ respond("")
+ }
+ runTest {
+ supabase.postgrest.rpc("function") {
+ method = RpcMethod.HEAD
+ schema = "schema"
+ headers["custom"] = "value"
+ params["custom"] = listOf("value")
+ }
+ }
+ }
+
+ @Test
+ fun testRpcParameters() {
+ val mockData = buildJsonObject {
+ put("key", "value")
+ }
+ val supabase = createMockedSupabaseClient(
+ configuration = configureClient
+ ) {
+ assertPathIs("/rpc/function", it.url.pathAfterVersion())
+ assertEquals(mockData, it.body.toJsonElement())
+ assertMethodIs(HttpMethod.Post, it.method)
+ assertEquals("schema", it.headers["Content-Profile"])
+ respond("")
+ }
+ runTest {
+ supabase.postgrest.rpc("function", mockData) {
+ schema = "schema"
+ }
+ }
+ }
+
+ private fun testClient(
+ table: String = "table",
+ request: suspend SupabaseClient.(table: String) -> PostgrestResult,
+ requestHandler: suspend MockRequestHandleScope.(HttpRequestData) -> HttpResponseData = { respond("")},
+ ) {
+ val supabase = createMockedSupabaseClient(
+ configuration = configureClient
+ ) {
+ assertPathIs("/$table", it.url.pathAfterVersion())
+ requestHandler(it)
+ }
+ runTest {
+ supabase.request(table)
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/Postgrest/src/commonTest/kotlin/Utils.kt b/Postgrest/src/commonTest/kotlin/Utils.kt
new file mode 100644
index 000000000..b1db90d84
--- /dev/null
+++ b/Postgrest/src/commonTest/kotlin/Utils.kt
@@ -0,0 +1,10 @@
+import io.github.jan.supabase.annotations.SupabaseInternal
+import io.github.jan.supabase.postgrest.PropertyConversionMethod
+import io.github.jan.supabase.postgrest.query.PostgrestRequestBuilder
+
+@SupabaseInternal
+inline fun postgrestRequest(propertyConversionMethod: PropertyConversionMethod = PropertyConversionMethod.CAMEL_CASE_TO_SNAKE_CASE, block: PostgrestRequestBuilder.() -> Unit): PostgrestRequestBuilder {
+ val filter = PostgrestRequestBuilder(propertyConversionMethod)
+ filter.block()
+ return filter
+}
\ No newline at end of file
diff --git a/Postgrest/src/commonTest/kotlin/io.github.jan.supabase.postgrest/request/DeleteRequestTest.kt b/Postgrest/src/commonTest/kotlin/request/DeleteRequestTest.kt
similarity index 89%
rename from Postgrest/src/commonTest/kotlin/io.github.jan.supabase.postgrest/request/DeleteRequestTest.kt
rename to Postgrest/src/commonTest/kotlin/request/DeleteRequestTest.kt
index adf3b0821..2a0da7ac1 100644
--- a/Postgrest/src/commonTest/kotlin/io.github.jan.supabase.postgrest/request/DeleteRequestTest.kt
+++ b/Postgrest/src/commonTest/kotlin/request/DeleteRequestTest.kt
@@ -1,7 +1,9 @@
-package io.github.jan.supabase.postgrest.request
+package request
import io.github.jan.supabase.postgrest.query.Count
import io.github.jan.supabase.postgrest.query.Returning
+import io.github.jan.supabase.postgrest.request.DeleteRequest
+import io.github.jan.supabase.postgrest.request.PostgrestRequest
import kotlin.test.Test
import kotlin.test.assertEquals
diff --git a/Postgrest/src/commonTest/kotlin/io.github.jan.supabase.postgrest/request/InsertRequestTest.kt b/Postgrest/src/commonTest/kotlin/request/InsertRequestTest.kt
similarity index 94%
rename from Postgrest/src/commonTest/kotlin/io.github.jan.supabase.postgrest/request/InsertRequestTest.kt
rename to Postgrest/src/commonTest/kotlin/request/InsertRequestTest.kt
index 65e898bb1..b2d080e18 100644
--- a/Postgrest/src/commonTest/kotlin/io.github.jan.supabase.postgrest/request/InsertRequestTest.kt
+++ b/Postgrest/src/commonTest/kotlin/request/InsertRequestTest.kt
@@ -1,7 +1,9 @@
-package io.github.jan.supabase.postgrest.request
+package request
import io.github.jan.supabase.postgrest.query.Count
import io.github.jan.supabase.postgrest.query.Returning
+import io.github.jan.supabase.postgrest.request.InsertRequest
+import io.github.jan.supabase.postgrest.request.PostgrestRequest
import kotlinx.serialization.json.JsonArray
import kotlin.test.Test
import kotlin.test.assertEquals
diff --git a/Postgrest/src/commonTest/kotlin/io.github.jan.supabase.postgrest/request/RpcRequestTest.kt b/Postgrest/src/commonTest/kotlin/request/RpcRequestTest.kt
similarity index 86%
rename from Postgrest/src/commonTest/kotlin/io.github.jan.supabase.postgrest/request/RpcRequestTest.kt
rename to Postgrest/src/commonTest/kotlin/request/RpcRequestTest.kt
index ba24a0e52..6857e646c 100644
--- a/Postgrest/src/commonTest/kotlin/io.github.jan.supabase.postgrest/request/RpcRequestTest.kt
+++ b/Postgrest/src/commonTest/kotlin/request/RpcRequestTest.kt
@@ -1,6 +1,8 @@
-package io.github.jan.supabase.postgrest.request
+package request
import io.github.jan.supabase.postgrest.query.Count
+import io.github.jan.supabase.postgrest.request.PostgrestRequest
+import io.github.jan.supabase.postgrest.request.RpcRequest
import io.ktor.http.HttpMethod
import kotlinx.serialization.json.JsonArray
import kotlin.test.Test
@@ -19,6 +21,7 @@ class RpcRequestTest {
count = Count.EXACT,
body = JsonArray(listOf()),
urlParams = mapOf("Key1" to "Value1"),
+ schema = "mySchema"
)
val count = (sut as RpcRequest).count
@@ -31,7 +34,7 @@ class RpcRequestTest {
"count=exact"
), sut.prefer
)
- assertEquals("", sut.schema)
+ assertEquals("mySchema", sut.schema)
assertEquals(mapOf("Key1" to "Value1"), sut.urlParams)
assertEquals(JsonArray(listOf()), sut.body)
}
@@ -43,6 +46,7 @@ class RpcRequestTest {
count = Count.EXACT,
body = JsonArray(listOf()),
urlParams = mapOf("Key1" to "Value1"),
+ schema = "mySchema"
)
val count = (sut as RpcRequest).count
assertNotNull(count)
@@ -53,7 +57,7 @@ class RpcRequestTest {
"count=exact"
), sut.prefer
)
- assertEquals("", sut.schema)
+ assertEquals("mySchema", sut.schema)
assertEquals(mapOf("Key1" to "Value1"), sut.urlParams)
assertEquals(JsonArray(listOf()), sut.body)
}
@@ -65,6 +69,7 @@ class RpcRequestTest {
count = null,
body = JsonArray(listOf()),
urlParams = mapOf("Key1" to "Value1"),
+ schema = "mySchema"
)
val count = (sut as RpcRequest).count
@@ -74,7 +79,7 @@ class RpcRequestTest {
listOf(
), sut.prefer
)
- assertEquals("", sut.schema)
+ assertEquals("mySchema", sut.schema)
assertEquals(mapOf("Key1" to "Value1"), sut.urlParams)
assertEquals(JsonArray(listOf()), sut.body)
}
@@ -86,6 +91,7 @@ class RpcRequestTest {
count = null,
body = null,
urlParams = mapOf("Key1" to "Value1"),
+ schema = "mySchema"
)
assertEquals("HEAD", sut.method.value)
@@ -93,7 +99,7 @@ class RpcRequestTest {
listOf(
), sut.prefer
)
- assertEquals("", sut.schema)
+ assertEquals("mySchema", sut.schema)
assertEquals(mapOf("Key1" to "Value1"), sut.urlParams)
assertNull(sut.body)
}
@@ -104,6 +110,7 @@ class RpcRequestTest {
count = null,
body = JsonArray(listOf()),
urlParams = mapOf("Key1" to "Value1"),
+ schema = "mySchema"
)
assertEquals("POST", sut.method.value)
@@ -111,7 +118,7 @@ class RpcRequestTest {
listOf(
), sut.prefer
)
- assertEquals("", sut.schema)
+ assertEquals("mySchema", sut.schema)
assertEquals(mapOf("Key1" to "Value1"), sut.urlParams)
assertEquals(JsonArray(listOf()), sut.body)
}
diff --git a/Postgrest/src/commonTest/kotlin/io.github.jan.supabase.postgrest/request/SelectRequestTest.kt b/Postgrest/src/commonTest/kotlin/request/SelectRequestTest.kt
similarity index 94%
rename from Postgrest/src/commonTest/kotlin/io.github.jan.supabase.postgrest/request/SelectRequestTest.kt
rename to Postgrest/src/commonTest/kotlin/request/SelectRequestTest.kt
index 8b2fe96a9..1effbc044 100644
--- a/Postgrest/src/commonTest/kotlin/io.github.jan.supabase.postgrest/request/SelectRequestTest.kt
+++ b/Postgrest/src/commonTest/kotlin/request/SelectRequestTest.kt
@@ -1,6 +1,8 @@
-package io.github.jan.supabase.postgrest.request
+package request
import io.github.jan.supabase.postgrest.query.Count
+import io.github.jan.supabase.postgrest.request.PostgrestRequest
+import io.github.jan.supabase.postgrest.request.SelectRequest
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
diff --git a/Postgrest/src/commonTest/kotlin/io.github.jan.supabase.postgrest/request/UpdateRequestTest.kt b/Postgrest/src/commonTest/kotlin/request/UpdateRequestTest.kt
similarity index 91%
rename from Postgrest/src/commonTest/kotlin/io.github.jan.supabase.postgrest/request/UpdateRequestTest.kt
rename to Postgrest/src/commonTest/kotlin/request/UpdateRequestTest.kt
index e6d398ca3..a46cb0b3e 100644
--- a/Postgrest/src/commonTest/kotlin/io.github.jan.supabase.postgrest/request/UpdateRequestTest.kt
+++ b/Postgrest/src/commonTest/kotlin/request/UpdateRequestTest.kt
@@ -1,7 +1,9 @@
-package io.github.jan.supabase.postgrest.request
+package request
import io.github.jan.supabase.postgrest.query.Count
import io.github.jan.supabase.postgrest.query.Returning
+import io.github.jan.supabase.postgrest.request.PostgrestRequest
+import io.github.jan.supabase.postgrest.request.UpdateRequest
import kotlinx.serialization.json.JsonArray
import kotlin.test.Test
import kotlin.test.assertEquals
diff --git a/Postgrest/src/wasmJsMain/kotlin/io/github/jan/supabase/postgrest/getColumnName.kt b/Postgrest/src/wasmJsMain/kotlin/io/github/jan/supabase/postgrest/getColumnName.kt
new file mode 100644
index 000000000..f732884bb
--- /dev/null
+++ b/Postgrest/src/wasmJsMain/kotlin/io/github/jan/supabase/postgrest/getColumnName.kt
@@ -0,0 +1,7 @@
+package io.github.jan.supabase.postgrest
+
+import io.github.jan.supabase.annotations.SupabaseInternal
+import kotlin.reflect.KProperty1
+
+@SupabaseInternal
+actual fun getSerialName(property: KProperty1) = property.name
diff --git a/README.md b/README.md
index 89bc5b701..5919bf68a 100644
--- a/README.md
+++ b/README.md
@@ -6,12 +6,12 @@ For information about supported Kotlin targets, see the corresponding module REA
[Migrating from version 1.4.X to 2.0.0](/MIGRATION.md)
-*Note: [WASM](https://github.com/supabase-community/supabase-kt/issues/86) build available: [2.5.4-wasm0](https://github.com/supabase-community/supabase-kt/releases/tag/2.5.4-wasm0)*
+*Note: The `WASM-JS` target for supported modules is only available for version 3.0.0 and above*
[![](https://img.shields.io/github/release/supabase-community/supabase-kt?label=stable)](https://github.com/supabase-community/supabase-kt/releases)
[![](https://badgen.net/github/release/supabase-community/supabase-kt?label=prerelease)](https://central.sonatype.com/search?q=io.github.jan.supabase&smo=true)
-[![Kotlin](https://img.shields.io/badge/kotlin-2.0.10-blue.svg?logo=kotlin)](http://kotlinlang.org)
-![https://img.shields.io/badge/ktor-2.3.12-blue](https://img.shields.io/badge/ktor-2.3.12-blue)
+[![Kotlin](https://img.shields.io/badge/kotlin-2.0.20-blue.svg?logo=kotlin)](http://kotlinlang.org)
+[![Ktor](https://img.shields.io/badge/ktor-3.0.0--rc--1-blue)](https://ktor.io/)
[![slack](https://img.shields.io/badge/slack-%23supabase--kt-purple.svg?logo=slack)](https://kotlinlang.slack.com/archives/C06QXPC7064)
### Links
@@ -34,7 +34,9 @@ For information about supported Kotlin targets, see the corresponding module REA
### Add one or more modules to your project
-**Available modules**: `gotrue-kt`, `postgrest-kt`, `functions-kt`, `storage-kt`, `realtime-kt`, `apollo-graphql`, `compose-auth`, `compose-auth-ui`, `coil-integration`, `imageloader-integration`
+**Available modules**: `gotrue-kt`*, `postgrest-kt`, `functions-kt`,
+`storage-kt`, `realtime-kt`, `apollo-graphql`, `compose-auth`,
+`compose-auth-ui`, `coil-integration`, `coil3-integration`, `imageloader-integration`
```kotlin
dependencies {
@@ -42,6 +44,8 @@ dependencies {
}
```
+\* After version 3.0.0, the module will be called `auth-kt`.
+
If you use multiple modules, you can use the bom dependency to get the correct versions for all
modules:
@@ -94,11 +98,15 @@ val iosMain by getting {
**Note:** It is recommended to use the same Ktor version as supabase-kt:
-![https://img.shields.io/badge/ktor-2.3.12-blue](https://img.shields.io/badge/ktor-2.3.12-blue)
+__For 3.0.0 and above:__
+[![Ktor](https://img.shields.io/badge/ktor-3.0.0--rc--1-blue)](https://ktor.io/)
+
+__For versions below 3.0.0:__
+[![Ktor](https://img.shields.io/badge/ktor-2.3.12-blue)](https://ktor.io/)
## Main Modules
-- [Authentication](/GoTrue)
+- [Authentication](/GoTrue)
- [Database/Postgrest](/Postgrest)
- [Storage](/Storage)
- [Realtime](/Realtime)
@@ -109,7 +117,8 @@ val iosMain by getting {
- [Apollo GraphQL integration](/plugins/ApolloGraphQL) - Creates an [Apollo GraphQL Client](https://github.com/apollographql/apollo-kotlin) for interacting with the Supabase API.
- [Compose Auth](/plugins/ComposeAuth) - Provides easy Native Google & Apple Auth for Compose Multiplatform targets.
- [Compose Auth UI](/plugins/ComposeAuthUI) - Provides UI Components for Compose Multiplatform.
-- [Coil Integration](/plugins/CoilIntegration) - Provides a [Coil](https://github.com/coil-kt/coil) Integration for displaying images stored in Supabase Storage.
+- [Coil Integration](/plugins/CoilIntegration) - Provides a [Coil2](https://github.com/coil-kt/coil) Integration for displaying images stored in Supabase Storage. Only supports Android.
+- [Coil3 Integration](/plugins/Coil3Integration) - Provides a [Coil3](https://github.com/coil-kt/coil) Integration for displaying images stored in Supabase Storage. Supports all Compose Multiplatform targets.
- [Compose-ImageLoader Integration](/plugins/ImageLoaderIntegration) - Provides a [Compose ImageLoader](https://github.com/qdsfdhvh/compose-imageloader) Integration for displaying images stored in Supabase Storage.
### Miscellaneous
diff --git a/Realtime/README.md b/Realtime/README.md
index bf1c2983d..3867f894d 100644
--- a/Realtime/README.md
+++ b/Realtime/README.md
@@ -4,23 +4,27 @@ Extends Supabase-kt with a multiplatform Realtime client.
Supported targets:
-| Target | **JVM** | **Android** | **JS** | **iOS** | **tvOS** | **watchOS** | **macOS** | **Windows** | **Linux** |
-| ------ | ------- | ----------- | ------ | ------- | -------- | ----------- | --------- | ----------- | --------- |
-| | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
+| Target | **JVM** | **Android** | **JS** | **Wasm** | **Apple** | **Windows** | **Linux** |
+|--------|---------|-------------|--------|----------|-----------|-------------|-----------|
+| Status | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
In-depth Kotlin targets
-**iOS:** iosArm64, iosSimulatorArm64, iosX64
-
**JS**: Browser, NodeJS
-**tvOS**: tvosArm64, tvosX64, tvosSimulatorArm64
+**Wasm**: wasm-js
+
+**Apple:**
+
+- iOS: iosArm64, iosSimulatorArm64, iosX64
+
+- tvOS: tvosArm64, tvosX64, tvosSimulatorArm64
-**watchOS**: watchosArm64, watchosX64, watchosSimulatorArm64
+- watchOS: watchosArm64, watchosX64, watchosSimulatorArm64
-**MacOS**: macosX64, macosArm64
+- MacOS: macosX64, macosArm64
**Windows**: mingwX64
diff --git a/Realtime/build.gradle.kts b/Realtime/build.gradle.kts
index ecfef9ab2..075fff6d1 100644
--- a/Realtime/build.gradle.kts
+++ b/Realtime/build.gradle.kts
@@ -16,7 +16,7 @@ kotlin {
sourceSets {
val commonMain by getting {
dependencies {
- addModules(SupabaseModule.GOTRUE)
+ addModules(SupabaseModule.AUTH)
api(project(":postgrest-kt"))
api(libs.ktor.client.websockets)
}
diff --git a/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/PostgresChangeFilter.kt b/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/PostgresChangeFilter.kt
index 4d5f42fa8..8b94dac8d 100644
--- a/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/PostgresChangeFilter.kt
+++ b/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/PostgresChangeFilter.kt
@@ -7,7 +7,6 @@ import io.github.jan.supabase.postgrest.query.filter.FilterOperator
/**
* Used to filter postgres changes
*/
-@Suppress("DEPRECATION")
class PostgresChangeFilter(private val event: String, private val schema: String) {
/**
@@ -20,7 +19,7 @@ class PostgresChangeFilter(private val event: String, private val schema: String
* E.g.: "user_id=eq.1"
*/
var filter: String? = null
- @Deprecated("Use the new `filter` method instead") set
+ private set
/**
* Filters the received changes in your table.
diff --git a/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/Realtime.kt b/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/Realtime.kt
index 37767e7fe..6ea5aa950 100644
--- a/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/Realtime.kt
+++ b/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/Realtime.kt
@@ -67,10 +67,7 @@ sealed interface Realtime : MainPlugin, CustomSerializationPlug
fun disconnect()
@SupabaseInternal
- fun RealtimeChannel.addChannel(channel: RealtimeChannel)
-
- @SupabaseInternal
- fun RealtimeChannel.deleteChannel(channel: RealtimeChannel)
+ fun Realtime.addChannel(channel: RealtimeChannel)
/**
* Unsubscribes and removes a channel from the [subscriptions]
@@ -152,8 +149,19 @@ sealed interface Realtime : MainPlugin, CustomSerializationPlug
* The current status of the realtime connection
*/
enum class Status {
+ /**
+ * [Realtime] is disconnected from Supabase Realtime
+ */
DISCONNECTED,
+
+ /**
+ * [Realtime] is connecting to Supabase Realtime
+ */
CONNECTING,
+
+ /**
+ * [Realtime] is connected to Supabase Realtime
+ */
CONNECTED,
}
diff --git a/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/RealtimeChannel.kt b/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/RealtimeChannel.kt
index 7212a1662..2c461805f 100644
--- a/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/RealtimeChannel.kt
+++ b/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/RealtimeChannel.kt
@@ -2,15 +2,14 @@ package io.github.jan.supabase.realtime
import io.github.jan.supabase.SupabaseClient
import io.github.jan.supabase.annotations.SupabaseInternal
-import io.github.jan.supabase.decode
import io.github.jan.supabase.encodeToJsonElement
-import io.github.jan.supabase.logging.e
-import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.callbackFlow
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.jsonObject
+import kotlin.reflect.KClass
+import kotlin.reflect.KType
+import kotlin.reflect.typeOf
/**
* Represents a realtime channel
@@ -32,6 +31,11 @@ sealed interface RealtimeChannel {
*/
val supabaseClient: SupabaseClient
+ /**
+ * The realtime instance
+ */
+ val realtime: Realtime
+
@SupabaseInternal
val callbackManager: CallbackManager
@@ -70,22 +74,70 @@ sealed interface RealtimeChannel {
*/
suspend fun untrack()
+ /**
+ * Non-inline variant of [postgresChangeFlow] for implementation and mocking purposes
+ */
+ @SupabaseInternal
+ fun RealtimeChannel.postgresChangeFlowInternal(action: KClass, schema: String, filter: PostgresChangeFilter.() -> Unit = {}): Flow
+
+ /**
+ * Non-inline variant of [broadcastFlow] for implementation and mocking purposes
+ */
+ @SupabaseInternal
+ fun RealtimeChannel.broadcastFlowInternal(type: KType, event: String): Flow
+
+ /**
+ * Listen for clients joining / leaving the channel using presences
+ *
+ * Example:
+ * ```kotlin
+ * val presenceChangeFlow = channel.presenceChangeFlow()
+ *
+ * presenceChangeFlow.collect {
+ * val joins = it.decodeJoinsAs()
+ * val leaves = it.decodeLeavesAs()
+ * }
+ * ```
+ */
+ fun presenceChangeFlow(): Flow
+
@SupabaseInternal
fun RealtimeChannel.addPostgresChange(data: PostgresJoinConfig)
@SupabaseInternal
fun RealtimeChannel.removePostgresChange(data: PostgresJoinConfig)
+ @SupabaseInternal
+ fun updateStatus(status: Status)
+
/**
* Represents the status of a channel
*/
enum class Status {
+ /**
+ * The [RealtimeChannel] is currently unsubscribed
+ */
UNSUBSCRIBED,
+
+ /**
+ * The [RealtimeChannel] is currently in the process of subscribing
+ */
SUBSCRIBING,
+
+ /**
+ * The [RealtimeChannel] is subscribed
+ */
SUBSCRIBED,
+
+ /**
+ * The [RealtimeChannel] is in the process of unsubscribing
+ */
UNSUBSCRIBING,
}
+ /**
+ * @see RealtimeChannel
+ */
@Suppress("UndocumentedPublicProperty")
companion object {
const val CHANNEL_EVENT_JOIN = "phx_join"
@@ -104,31 +156,6 @@ sealed interface RealtimeChannel {
}
-/**
- * Listen for clients joining / leaving the channel using presences
- *
- * Example:
- * ```kotlin
- * val presenceChangeFlow = channel.presenceChangeFlow()
- *
- * presenceChangeFlow.collect {
- * val joins = it.decodeJoinsAs()
- * val leaves = it.decodeLeavesAs()
- * }
- * ```
- */
-@OptIn(SupabaseInternal::class)
-fun RealtimeChannel.presenceChangeFlow(): Flow {
- return callbackFlow {
- val callback: (PresenceAction) -> Unit = { action ->
- trySend(action)
- }
-
- val id = callbackManager.addPresenceCallback(callback)
- awaitClose { callbackManager.removeCallbackById(id) }
- }
-}
-
/**
* Listen for postgres changes in a channel.
*
@@ -144,34 +171,10 @@ fun RealtimeChannel.presenceChangeFlow(): Flow {
* @param T The event type you want to listen to (e.g. [PostgresAction.Update] for updates or only [PostgresAction] for all)
* @param schema The schema name of the table that is being monitored. For normal supabase tables that might be "public".
*/
-@OptIn(SupabaseInternal::class)
-inline fun RealtimeChannel.postgresChangeFlow(schema: String, filter: PostgresChangeFilter.() -> Unit = {}): Flow {
- if(status.value == RealtimeChannel.Status.SUBSCRIBED) error("You cannot call postgresChangeFlow after joining the channel")
- val event = when(T::class) {
- PostgresAction.Insert::class -> "INSERT"
- PostgresAction.Update::class -> "UPDATE"
- PostgresAction.Delete::class -> "DELETE"
- PostgresAction.Select::class -> "SELECT"
- PostgresAction::class -> "*"
- else -> error("Unknown event type ${T::class}")
- }
- val postgrestBuilder = PostgresChangeFilter(event, schema).apply(filter)
- val config = postgrestBuilder.buildConfig()
- addPostgresChange(config)
- return callbackFlow {
- val callback: (PostgresAction) -> Unit = {
- if (it is T) {
- trySend(it)
- }
- }
-
- val id = callbackManager.addPostgresCallback(config, callback)
- awaitClose {
- callbackManager.removeCallbackById(id)
- removePostgresChange(config)
- }
- }
-}
+inline fun RealtimeChannel.postgresChangeFlow(
+ schema: String,
+ noinline filter: PostgresChangeFilter.() -> Unit = {}
+): Flow = postgresChangeFlowInternal(T::class, schema, filter)
/**
* Broadcasts can be messages sent by other clients within the same channel under a specific [event].
@@ -187,18 +190,7 @@ inline fun RealtimeChannel.postgresChangeFlow(schem
* @param event When a message is sent by another client, it will be sent under a specific event. This is the event that you want to listen to
*/
@OptIn(SupabaseInternal::class)
-inline fun RealtimeChannel.broadcastFlow(event: String): Flow = callbackFlow {
- val id = callbackManager.addBroadcastCallback(event) {
- val decodedValue = try {
- supabaseClient.realtime.serializer.decode(it.toString())
- } catch(e: Exception) {
- Realtime.logger.e(e) { "Couldn't decode $this as ${T::class.simpleName}. The corresponding handler wasn't called" }
- null
- }
- decodedValue?.let { value -> trySend(value) }
- }
- awaitClose { callbackManager.removeCallbackById(id) }
-}
+inline fun RealtimeChannel.broadcastFlow(event: String): Flow = broadcastFlowInternal(typeOf(), event)
/**
* Sends a message to everyone who joined the channel. Can be used even if you aren't connected to the channel.
diff --git a/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/RealtimeChannelImpl.kt b/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/RealtimeChannelImpl.kt
index 6b1c171e3..27bdca12a 100644
--- a/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/RealtimeChannelImpl.kt
+++ b/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/RealtimeChannelImpl.kt
@@ -1,33 +1,31 @@
package io.github.jan.supabase.realtime
import io.github.jan.supabase.annotations.SupabaseInternal
+import io.github.jan.supabase.auth.resolveAccessToken
import io.github.jan.supabase.collections.AtomicMutableList
-import io.github.jan.supabase.decodeIfNotEmptyOrDefault
-import io.github.jan.supabase.gotrue.resolveAccessToken
import io.github.jan.supabase.logging.d
import io.github.jan.supabase.logging.e
-import io.github.jan.supabase.logging.w
import io.github.jan.supabase.putJsonObject
import io.github.jan.supabase.realtime.data.BroadcastApiBody
import io.github.jan.supabase.realtime.data.BroadcastApiMessage
-import io.github.jan.supabase.realtime.data.PostgresActionData
-import io.github.jan.supabase.supabaseJson
+import io.github.jan.supabase.realtime.event.RealtimeEvent
import io.ktor.client.statement.bodyAsText
import io.ktor.http.headers
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.first
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.buildJsonObject
-import kotlinx.serialization.json.decodeFromJsonElement
import kotlinx.serialization.json.encodeToJsonElement
-import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
-import kotlinx.serialization.json.jsonPrimitive
-import kotlinx.serialization.json.longOrNull
import kotlinx.serialization.json.put
import kotlinx.serialization.json.putJsonObject
+import kotlin.reflect.KClass
+import kotlin.reflect.KType
internal class RealtimeChannelImpl(
private val realtimeImpl: RealtimeImpl,
@@ -42,6 +40,7 @@ internal class RealtimeChannelImpl(
override val callbackManager = CallbackManagerImpl(realtimeImpl.serializer)
private val _status = MutableStateFlow(RealtimeChannel.Status.UNSUBSCRIBED)
override val status = _status.asStateFlow()
+ override val realtime: Realtime = realtimeImpl
override val supabaseClient = realtimeImpl.supabaseClient
@@ -79,70 +78,13 @@ internal class RealtimeChannelImpl(
}
@OptIn(SupabaseInternal::class)
- fun onMessage(message: RealtimeMessage) {
- if(message.eventType == null) {
- Realtime.logger.e { "Received message without event type: $message" }
+ suspend fun onMessage(message: RealtimeMessage) {
+ val event = RealtimeEvent.resolveEvent(message)
+ if(event == null) {
+ Realtime.logger.e { "Received message without event: $message" }
return
}
- when(message.eventType) {
- RealtimeMessage.EventType.TOKEN_EXPIRED -> {
- Realtime.logger.w { "Received token expired event. This should not happen, please report this warning." }
- }
- RealtimeMessage.EventType.SYSTEM -> {
- Realtime.logger.d { "Subscribed to channel ${message.topic}" }
- _status.value = RealtimeChannel.Status.SUBSCRIBED
- }
- RealtimeMessage.EventType.SYSTEM_REPLY -> {
- Realtime.logger.d { "Received system reply: ${message.payload}." }
- if(status.value == RealtimeChannel.Status.UNSUBSCRIBING) {
- _status.value = RealtimeChannel.Status.UNSUBSCRIBED
- Realtime.logger.d { "Unsubscribed from channel ${message.topic}" }
- }
- }
- RealtimeMessage.EventType.POSTGRES_SERVER_CHANGES -> { //check if the server postgres_changes match with the client's and add the given id to the postgres change objects (to identify them later in the events)
- val serverPostgresChanges = message.payload["response"]?.jsonObject?.get("postgres_changes")?.jsonArray?.let { Json.decodeFromJsonElement>(it) } ?: listOf() //server postgres changes
- callbackManager.setServerChanges(serverPostgresChanges)
- if(status.value != RealtimeChannel.Status.SUBSCRIBED) {
- Realtime.logger.d { "Joined channel ${message.topic}" }
- _status.value = RealtimeChannel.Status.SUBSCRIBED
- }
- }
- RealtimeMessage.EventType.POSTGRES_CHANGES -> {
- val data = message.payload["data"]?.jsonObject ?: return
- val ids = message.payload["ids"]?.jsonArray?.mapNotNull { it.jsonPrimitive.longOrNull } ?: emptyList() //the ids of the matching postgres changes
- val postgresAction = supabaseJson.decodeFromJsonElement(data)
- val action = when(data["type"]?.jsonPrimitive?.content ?: "") {
- "UPDATE" -> PostgresAction.Update(postgresAction.record ?: error("Received no record on update event"), postgresAction.oldRecord ?: error("Received no old record on update event"), postgresAction.columns, postgresAction.commitTimestamp, realtimeImpl.serializer)
- "DELETE" -> PostgresAction.Delete(postgresAction.oldRecord ?: error("Received no old record on delete event"), postgresAction.columns, postgresAction.commitTimestamp, realtimeImpl.serializer)
- "INSERT" -> PostgresAction.Insert(postgresAction.record ?: error("Received no record on update event"), postgresAction.columns, postgresAction.commitTimestamp, realtimeImpl.serializer)
- "SELECT" -> PostgresAction.Select(postgresAction.record ?: error("Received no record on update event"), postgresAction.columns, postgresAction.commitTimestamp, realtimeImpl.serializer)
- else -> error("Unknown event type ${message.event}")
- }
- callbackManager.triggerPostgresChange(ids, action)
- }
- RealtimeMessage.EventType.BROADCAST -> {
- val event = message.payload["event"]?.jsonPrimitive?.content ?: ""
- callbackManager.triggerBroadcast(event, message.payload["payload"]?.jsonObject ?: JsonObject(mutableMapOf()))
- }
- RealtimeMessage.EventType.CLOSE -> {
- realtimeImpl.run {
- deleteChannel(this@RealtimeChannelImpl)
- }
- Realtime.logger.d { "Unsubscribed from channel ${message.topic}" }
- }
- RealtimeMessage.EventType.ERROR -> {
- Realtime.logger.e { "Received an error in channel ${message.topic}. That could be as a result of an invalid access token" }
- }
- RealtimeMessage.EventType.PRESENCE_DIFF -> {
- val joins = message.payload["joins"]?.jsonObject?.decodeIfNotEmptyOrDefault(mapOf()) ?: emptyMap()
- val leaves = message.payload["leaves"]?.jsonObject?.decodeIfNotEmptyOrDefault(mapOf()) ?: emptyMap()
- callbackManager.triggerPresenceDiff(joins, leaves)
- }
- RealtimeMessage.EventType.PRESENCE_STATE -> {
- val joins = message.payload.decodeIfNotEmptyOrDefault(mapOf())
- callbackManager.triggerPresenceDiff(joins, mapOf())
- }
- }
+ event.handle(this, message)
}
override suspend fun unsubscribe() {
@@ -218,5 +160,63 @@ internal class RealtimeChannelImpl(
)
}
+ @Suppress("UNCHECKED_CAST")
+ override fun RealtimeChannel.postgresChangeFlowInternal(
+ action: KClass,
+ schema: String,
+ filter: PostgresChangeFilter.() -> Unit
+ ): Flow {
+ if(status.value == RealtimeChannel.Status.SUBSCRIBED) error("You cannot call postgresChangeFlow after joining the channel")
+ val event = when(action) {
+ PostgresAction.Insert::class -> "INSERT"
+ PostgresAction.Update::class -> "UPDATE"
+ PostgresAction.Delete::class -> "DELETE"
+ PostgresAction.Select::class -> "SELECT"
+ PostgresAction::class -> "*"
+ else -> error("Unknown event type $action")
+ }
+ val postgrestBuilder = PostgresChangeFilter(event, schema).apply(filter)
+ val config = postgrestBuilder.buildConfig()
+ addPostgresChange(config)
+ return callbackFlow {
+ val callback: (PostgresAction) -> Unit = {
+ if (action.isInstance(it)) {
+ trySend(it as T)
+ }
+ }
+
+ val id = callbackManager.addPostgresCallback(config, callback)
+ awaitClose {
+ callbackManager.removeCallbackById(id)
+ removePostgresChange(config)
+ }
+ }
+ }
+
+ override fun RealtimeChannel.broadcastFlowInternal(type: KType, event: String): Flow = callbackFlow {
+ val id = callbackManager.addBroadcastCallback(event) {
+ val decodedValue = try {
+ supabaseClient.realtime.serializer.decode(type, it.toString())
+ } catch(e: Exception) {
+ Realtime.logger.e(e) { "Couldn't decode $it as $type. The corresponding handler wasn't called" }
+ null
+ }
+ decodedValue?.let { value -> trySend(value) }
+ }
+ awaitClose { callbackManager.removeCallbackById(id) }
+ }
+
+ override fun presenceChangeFlow(): Flow = callbackFlow {
+ val callback: (PresenceAction) -> Unit = { action ->
+ trySend(action)
+ }
+ val id = callbackManager.addPresenceCallback(callback)
+ awaitClose { callbackManager.removeCallbackById(id) }
+ }
+
+ override fun updateStatus(status: RealtimeChannel.Status) {
+ _status.value = status
+ }
+
}
diff --git a/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/RealtimeExt.kt b/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/RealtimeExt.kt
index d43ef41f8..39a1d115d 100644
--- a/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/RealtimeExt.kt
+++ b/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/RealtimeExt.kt
@@ -30,7 +30,7 @@ internal fun List>.producer(data: Data): String =
/**
* Listens for presence changes and caches the presences based on their keys. This function automatically handles joins and leaves.
*
- * If you want more control, use the [presenceChangeFlow] function.
+ * If you want more control, use the [RealtimeChannel.presenceChangeFlow] function.
* @return a [Flow] of the current presences in a list. This list is updated and emitted whenever a presence joins or leaves.
*/
inline fun RealtimeChannel.presenceDataFlow(): Flow> {
diff --git a/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/RealtimeImpl.kt b/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/RealtimeImpl.kt
index 66b4d560e..816d362a7 100644
--- a/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/RealtimeImpl.kt
+++ b/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/RealtimeImpl.kt
@@ -2,43 +2,35 @@ package io.github.jan.supabase.realtime
import io.github.jan.supabase.SupabaseClient
import io.github.jan.supabase.annotations.SupabaseInternal
+import io.github.jan.supabase.auth.Auth
+import io.github.jan.supabase.auth.SessionStatus
import io.github.jan.supabase.buildUrl
import io.github.jan.supabase.collections.AtomicMutableMap
import io.github.jan.supabase.exceptions.RestException
import io.github.jan.supabase.exceptions.UnknownRestException
-import io.github.jan.supabase.gotrue.Auth
-import io.github.jan.supabase.gotrue.SessionStatus
import io.github.jan.supabase.logging.d
import io.github.jan.supabase.logging.e
import io.github.jan.supabase.logging.i
import io.github.jan.supabase.logging.w
import io.github.jan.supabase.realtime.websocket.KtorRealtimeWebsocketFactory
import io.github.jan.supabase.realtime.websocket.RealtimeWebsocket
-import io.github.jan.supabase.supabaseJson
-import io.ktor.client.plugins.websocket.DefaultClientWebSocketSession
-import io.ktor.client.plugins.websocket.sendSerialized
import io.ktor.client.statement.HttpResponse
import io.ktor.http.URLProtocol
import io.ktor.http.path
-import io.ktor.websocket.Frame
-import io.ktor.websocket.readText
import kotlinx.atomicfu.atomic
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
-import kotlinx.coroutines.cancel
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.isActive
-import kotlinx.coroutines.job
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.serialization.json.buildJsonObject
-import kotlin.time.Duration.Companion.milliseconds
@PublishedApi internal class RealtimeImpl(override val supabaseClient: SupabaseClient, override val config: Realtime.Config) : Realtime {
@@ -163,7 +155,7 @@ import kotlin.time.Duration.Companion.milliseconds
_status.value = Realtime.Status.DISCONNECTED
}
- private fun onMessage(message: RealtimeMessage) {
+ private suspend fun onMessage(message: RealtimeMessage) {
Realtime.logger.d { "Received message $message" }
val channel = subscriptions[message.topic] as? RealtimeChannelImpl
if(message.ref?.toIntOrNull() == heartbeatRef) {
@@ -208,11 +200,6 @@ import kotlin.time.Duration.Companion.milliseconds
}
}
- @SupabaseInternal
- override fun RealtimeChannel.deleteChannel(channel: RealtimeChannel) {
- _subscriptions.remove(channel.topic)
- }
-
override suspend fun removeAllChannels() {
_subscriptions.forEach { (_, it) ->
if(it.status.value == RealtimeChannel.Status.SUBSCRIBED) {
@@ -227,7 +214,7 @@ import kotlin.time.Duration.Companion.milliseconds
}
@SupabaseInternal
- override fun RealtimeChannel.addChannel(channel: RealtimeChannel) {
+ override fun Realtime.addChannel(channel: RealtimeChannel) {
_subscriptions[channel.topic] = channel
}
diff --git a/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/RealtimeMessage.kt b/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/RealtimeMessage.kt
index 6832f15af..379017d35 100644
--- a/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/RealtimeMessage.kt
+++ b/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/RealtimeMessage.kt
@@ -2,36 +2,11 @@ package io.github.jan.supabase.realtime
import io.github.jan.supabase.annotations.SupabaseInternal
import kotlinx.serialization.Serializable
-import kotlinx.serialization.Transient
import kotlinx.serialization.json.JsonObject
-import kotlinx.serialization.json.jsonObject
-import kotlinx.serialization.json.jsonPrimitive
/**
* Represents a message retrieved by the [RealtimeChannel]
*/
@Serializable
@SupabaseInternal
-data class RealtimeMessage(val topic: String, val event: String, val payload: JsonObject, val ref: String?) {
-
- @Transient
- val eventType: EventType? = when {
- event == RealtimeChannel.CHANNEL_EVENT_SYSTEM && payload["status"]?.jsonPrimitive?.content == "ok" -> EventType.SYSTEM
- event == RealtimeChannel.CHANNEL_EVENT_REPLY && payload["response"]?.jsonObject?.containsKey(RealtimeChannel.CHANNEL_EVENT_POSTGRES_CHANGES) ?: false -> EventType.POSTGRES_SERVER_CHANGES
- event == RealtimeChannel.CHANNEL_EVENT_REPLY && payload["status"]?.jsonPrimitive?.content == "ok" -> EventType.SYSTEM_REPLY
- event == RealtimeChannel.CHANNEL_EVENT_POSTGRES_CHANGES -> EventType.POSTGRES_CHANGES
- event == RealtimeChannel.CHANNEL_EVENT_BROADCAST -> EventType.BROADCAST
- event == RealtimeChannel.CHANNEL_EVENT_CLOSE -> EventType.CLOSE
- event == RealtimeChannel.CHANNEL_EVENT_ERROR -> EventType.ERROR
- event == RealtimeChannel.CHANNEL_EVENT_PRESENCE_DIFF -> EventType.PRESENCE_DIFF
- event == RealtimeChannel.CHANNEL_EVENT_PRESENCE_STATE -> EventType.PRESENCE_STATE
- event == RealtimeChannel.CHANNEL_EVENT_SYSTEM && payload["message"]?.jsonPrimitive?.content?.contains("access token has expired") ?: false -> EventType.TOKEN_EXPIRED
- else -> null
- }
-
- enum class EventType {
- SYSTEM, SYSTEM_REPLY, POSTGRES_SERVER_CHANGES, POSTGRES_CHANGES, BROADCAST, CLOSE, ERROR, PRESENCE_DIFF, PRESENCE_STATE, TOKEN_EXPIRED
- }
-
-}
-
+data class RealtimeMessage(val topic: String, val event: String, val payload: JsonObject, val ref: String?)
\ No newline at end of file
diff --git a/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/event/RBroadcastEvent.kt b/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/event/RBroadcastEvent.kt
new file mode 100644
index 000000000..e163eda1b
--- /dev/null
+++ b/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/event/RBroadcastEvent.kt
@@ -0,0 +1,23 @@
+package io.github.jan.supabase.realtime.event
+
+import io.github.jan.supabase.realtime.RealtimeChannel
+import io.github.jan.supabase.realtime.RealtimeMessage
+import kotlinx.serialization.json.JsonObject
+import kotlinx.serialization.json.jsonObject
+import kotlinx.serialization.json.jsonPrimitive
+
+/**
+ * Handles broadcast events
+ */
+data object RBroadcastEvent : RealtimeEvent {
+
+ override suspend fun handle(channel: RealtimeChannel, message: RealtimeMessage) {
+ val event = message.payload["event"]?.jsonPrimitive?.content ?: ""
+ channel.callbackManager.triggerBroadcast(event, message.payload["payload"]?.jsonObject ?: JsonObject(mutableMapOf()))
+ }
+
+ override fun appliesTo(message: RealtimeMessage): Boolean {
+ return message.event == RealtimeChannel.CHANNEL_EVENT_BROADCAST
+ }
+
+}
\ No newline at end of file
diff --git a/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/event/RCloseEvent.kt b/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/event/RCloseEvent.kt
new file mode 100644
index 000000000..1ef7d52c8
--- /dev/null
+++ b/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/event/RCloseEvent.kt
@@ -0,0 +1,22 @@
+package io.github.jan.supabase.realtime.event
+
+import io.github.jan.supabase.logging.d
+import io.github.jan.supabase.realtime.Realtime
+import io.github.jan.supabase.realtime.RealtimeChannel
+import io.github.jan.supabase.realtime.RealtimeMessage
+
+/**
+ * Event that handles the closing of a channel
+ */
+data object RCloseEvent : RealtimeEvent {
+
+ override suspend fun handle(channel: RealtimeChannel, message: RealtimeMessage) {
+ channel.realtime.removeChannel(channel)
+ Realtime.logger.d { "Unsubscribed from channel ${message.topic}" }
+ }
+
+ override fun appliesTo(message: RealtimeMessage): Boolean {
+ return message.event == RealtimeChannel.CHANNEL_EVENT_CLOSE
+ }
+
+}
\ No newline at end of file
diff --git a/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/event/RErrorEvent.kt b/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/event/RErrorEvent.kt
new file mode 100644
index 000000000..1a926475b
--- /dev/null
+++ b/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/event/RErrorEvent.kt
@@ -0,0 +1,21 @@
+package io.github.jan.supabase.realtime.event
+
+import io.github.jan.supabase.logging.e
+import io.github.jan.supabase.realtime.Realtime
+import io.github.jan.supabase.realtime.RealtimeChannel
+import io.github.jan.supabase.realtime.RealtimeMessage
+
+/**
+ * Event that handles an error event
+ */
+data object RErrorEvent : RealtimeEvent {
+
+ override suspend fun handle(channel: RealtimeChannel, message: RealtimeMessage) {
+ Realtime.logger.e { "Received an error in channel ${message.topic}. That could be as a result of an invalid access token" }
+ }
+
+ override fun appliesTo(message: RealtimeMessage): Boolean {
+ return message.event == RealtimeChannel.CHANNEL_EVENT_ERROR
+ }
+
+}
\ No newline at end of file
diff --git a/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/event/RPostgresChangesEvent.kt b/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/event/RPostgresChangesEvent.kt
new file mode 100644
index 000000000..ea318ba9b
--- /dev/null
+++ b/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/event/RPostgresChangesEvent.kt
@@ -0,0 +1,59 @@
+package io.github.jan.supabase.realtime.event
+
+import io.github.jan.supabase.realtime.PostgresAction
+import io.github.jan.supabase.realtime.RealtimeChannel
+import io.github.jan.supabase.realtime.RealtimeMessage
+import io.github.jan.supabase.realtime.data.PostgresActionData
+import io.github.jan.supabase.realtime.realtime
+import io.github.jan.supabase.supabaseJson
+import kotlinx.serialization.json.decodeFromJsonElement
+import kotlinx.serialization.json.jsonArray
+import kotlinx.serialization.json.jsonObject
+import kotlinx.serialization.json.jsonPrimitive
+import kotlinx.serialization.json.longOrNull
+
+/**
+ * Handles postgres changes events
+ */
+data object RPostgresChangesEvent : RealtimeEvent {
+
+ override suspend fun handle(channel: RealtimeChannel, message: RealtimeMessage) {
+ val data = message.payload["data"]?.jsonObject ?: return
+ val ids = message.payload["ids"]?.jsonArray?.mapNotNull { it.jsonPrimitive.longOrNull } ?: emptyList() //the ids of the matching postgres changes
+ val postgresAction = supabaseJson.decodeFromJsonElement(data)
+ val action = when(data["type"]?.jsonPrimitive?.content ?: "") {
+ "UPDATE" -> PostgresAction.Update(
+ postgresAction.record ?: error("Received no record on update event"),
+ postgresAction.oldRecord ?: error("Received no old record on update event"),
+ postgresAction.columns,
+ postgresAction.commitTimestamp,
+ channel.supabaseClient.realtime.serializer
+ )
+ "DELETE" -> PostgresAction.Delete(
+ postgresAction.oldRecord ?: error("Received no old record on delete event"),
+ postgresAction.columns,
+ postgresAction.commitTimestamp,
+ channel.supabaseClient.realtime.serializer
+ )
+ "INSERT" -> PostgresAction.Insert(
+ postgresAction.record ?: error("Received no record on update event"),
+ postgresAction.columns,
+ postgresAction.commitTimestamp,
+ channel.supabaseClient.realtime.serializer
+ )
+ "SELECT" -> PostgresAction.Select(
+ postgresAction.record ?: error("Received no record on update event"),
+ postgresAction.columns,
+ postgresAction.commitTimestamp,
+ channel.supabaseClient.realtime.serializer
+ )
+ else -> error("Unknown event type ${message.event}")
+ }
+ channel.callbackManager.triggerPostgresChange(ids, action)
+ }
+
+ override fun appliesTo(message: RealtimeMessage): Boolean {
+ return message.event == RealtimeChannel.CHANNEL_EVENT_POSTGRES_CHANGES
+ }
+
+}
\ No newline at end of file
diff --git a/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/event/RPostgresServerChangesEvent.kt b/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/event/RPostgresServerChangesEvent.kt
new file mode 100644
index 000000000..0f8834ef2
--- /dev/null
+++ b/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/event/RPostgresServerChangesEvent.kt
@@ -0,0 +1,33 @@
+package io.github.jan.supabase.realtime.event
+
+import io.github.jan.supabase.logging.d
+import io.github.jan.supabase.realtime.PostgresJoinConfig
+import io.github.jan.supabase.realtime.Realtime
+import io.github.jan.supabase.realtime.RealtimeChannel
+import io.github.jan.supabase.realtime.RealtimeMessage
+import kotlinx.serialization.json.Json
+import kotlinx.serialization.json.decodeFromJsonElement
+import kotlinx.serialization.json.jsonArray
+import kotlinx.serialization.json.jsonObject
+
+/**
+ * Event that handles the server changes
+ */
+data object RPostgresServerChangesEvent : RealtimeEvent {
+
+ override suspend fun handle(channel: RealtimeChannel, message: RealtimeMessage) {
+ val serverPostgresChanges = message.payload["response"]?.jsonObject?.get("postgres_changes")?.jsonArray?.let { Json.decodeFromJsonElement>(it) } ?: listOf() //server postgres changes
+ channel.callbackManager.setServerChanges(serverPostgresChanges)
+ if(channel.status.value != RealtimeChannel.Status.SUBSCRIBED) {
+ Realtime.logger.d { "Joined channel ${message.topic}" }
+ channel.updateStatus(RealtimeChannel.Status.SUBSCRIBED)
+ }
+ }
+
+ override fun appliesTo(message: RealtimeMessage): Boolean {
+ return message.event == RealtimeChannel.CHANNEL_EVENT_REPLY && message.payload["response"]?.jsonObject?.containsKey(
+ RealtimeChannel.CHANNEL_EVENT_POSTGRES_CHANGES
+ ) ?: false
+ }
+
+}
diff --git a/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/event/RPresenceDiffEvent.kt b/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/event/RPresenceDiffEvent.kt
new file mode 100644
index 000000000..7d2be3910
--- /dev/null
+++ b/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/event/RPresenceDiffEvent.kt
@@ -0,0 +1,24 @@
+package io.github.jan.supabase.realtime.event
+
+import io.github.jan.supabase.decodeIfNotEmptyOrDefault
+import io.github.jan.supabase.realtime.Presence
+import io.github.jan.supabase.realtime.RealtimeChannel
+import io.github.jan.supabase.realtime.RealtimeMessage
+import kotlinx.serialization.json.jsonObject
+
+/**
+ * Event that handles the presence diff event
+ */
+data object RPresenceDiffEvent : RealtimeEvent {
+
+ override suspend fun handle(channel: RealtimeChannel, message: RealtimeMessage) {
+ val joins = message.payload["joins"]?.jsonObject?.decodeIfNotEmptyOrDefault(mapOf()) ?: emptyMap()
+ val leaves = message.payload["leaves"]?.jsonObject?.decodeIfNotEmptyOrDefault(mapOf()) ?: emptyMap()
+ channel.callbackManager.triggerPresenceDiff(joins, leaves)
+ }
+
+ override fun appliesTo(message: RealtimeMessage): Boolean {
+ return message.event == RealtimeChannel.CHANNEL_EVENT_PRESENCE_DIFF
+ }
+
+}
\ No newline at end of file
diff --git a/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/event/RPresenceStateEvent.kt b/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/event/RPresenceStateEvent.kt
new file mode 100644
index 000000000..53b65dbe1
--- /dev/null
+++ b/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/event/RPresenceStateEvent.kt
@@ -0,0 +1,22 @@
+package io.github.jan.supabase.realtime.event
+
+import io.github.jan.supabase.decodeIfNotEmptyOrDefault
+import io.github.jan.supabase.realtime.Presence
+import io.github.jan.supabase.realtime.RealtimeChannel
+import io.github.jan.supabase.realtime.RealtimeMessage
+
+/**
+ * Event that handles the presence state event
+ */
+data object RPresenceStateEvent : RealtimeEvent {
+
+ override suspend fun handle(channel: RealtimeChannel, message: RealtimeMessage) {
+ val joins = message.payload.decodeIfNotEmptyOrDefault(mapOf())
+ channel.callbackManager.triggerPresenceDiff(joins, mapOf())
+ }
+
+ override fun appliesTo(message: RealtimeMessage): Boolean {
+ return message.event == RealtimeChannel.CHANNEL_EVENT_PRESENCE_STATE
+ }
+
+}
\ No newline at end of file
diff --git a/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/event/RSystemEvent.kt b/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/event/RSystemEvent.kt
new file mode 100644
index 000000000..39b2828f0
--- /dev/null
+++ b/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/event/RSystemEvent.kt
@@ -0,0 +1,23 @@
+package io.github.jan.supabase.realtime.event
+
+import io.github.jan.supabase.logging.d
+import io.github.jan.supabase.realtime.Realtime
+import io.github.jan.supabase.realtime.RealtimeChannel
+import io.github.jan.supabase.realtime.RealtimeMessage
+import kotlinx.serialization.json.jsonPrimitive
+
+/**
+ * Event that handles the system event
+ */
+data object RSystemEvent : RealtimeEvent {
+
+ override suspend fun handle(channel: RealtimeChannel, message: RealtimeMessage) {
+ Realtime.logger.d { "Subscribed to channel ${message.topic}" }
+ channel.updateStatus(RealtimeChannel.Status.SUBSCRIBED)
+ }
+
+ override fun appliesTo(message: RealtimeMessage): Boolean {
+ return message.event == RealtimeChannel.CHANNEL_EVENT_SYSTEM && message.payload["status"]?.jsonPrimitive?.content == "ok"
+ }
+
+}
diff --git a/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/event/RSystemReplyEvent.kt b/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/event/RSystemReplyEvent.kt
new file mode 100644
index 000000000..715e85ae4
--- /dev/null
+++ b/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/event/RSystemReplyEvent.kt
@@ -0,0 +1,26 @@
+package io.github.jan.supabase.realtime.event
+
+import io.github.jan.supabase.logging.d
+import io.github.jan.supabase.realtime.Realtime
+import io.github.jan.supabase.realtime.RealtimeChannel
+import io.github.jan.supabase.realtime.RealtimeMessage
+import kotlinx.serialization.json.jsonPrimitive
+
+/**
+ * Event that handles the system reply event
+ */
+data object RSystemReplyEvent : RealtimeEvent {
+
+ override suspend fun handle(channel: RealtimeChannel, message: RealtimeMessage) {
+ Realtime.logger.d { "Received system reply: ${message.payload}." }
+ if(channel.status.value == RealtimeChannel.Status.UNSUBSCRIBING) {
+ channel.updateStatus(RealtimeChannel.Status.UNSUBSCRIBED)
+ Realtime.logger.d { "Unsubscribed from channel ${message.topic}" }
+ }
+ }
+
+ override fun appliesTo(message: RealtimeMessage): Boolean {
+ return message.event == RealtimeChannel.CHANNEL_EVENT_REPLY && message.payload["status"]?.jsonPrimitive?.content == "ok"
+ }
+
+}
\ No newline at end of file
diff --git a/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/event/RTokenExpiredEvent.kt b/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/event/RTokenExpiredEvent.kt
new file mode 100644
index 000000000..fd3802c19
--- /dev/null
+++ b/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/event/RTokenExpiredEvent.kt
@@ -0,0 +1,22 @@
+package io.github.jan.supabase.realtime.event
+
+import io.github.jan.supabase.logging.w
+import io.github.jan.supabase.realtime.Realtime
+import io.github.jan.supabase.realtime.RealtimeChannel
+import io.github.jan.supabase.realtime.RealtimeMessage
+import kotlinx.serialization.json.jsonPrimitive
+
+/**
+ * Event that handles the token expired event
+ */
+data object RTokenExpiredEvent : RealtimeEvent {
+
+ override suspend fun handle(channel: RealtimeChannel, message: RealtimeMessage) {
+ Realtime.logger.w { "Received token expired event. This should not happen, please report this warning." }
+ }
+
+ override fun appliesTo(message: RealtimeMessage): Boolean {
+ return message.event == RealtimeChannel.CHANNEL_EVENT_SYSTEM && message.payload["message"]?.jsonPrimitive?.content?.contains("access token has expired") ?: false
+ }
+
+}
\ No newline at end of file
diff --git a/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/event/RealtimeEvent.kt b/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/event/RealtimeEvent.kt
new file mode 100644
index 000000000..d74311006
--- /dev/null
+++ b/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/event/RealtimeEvent.kt
@@ -0,0 +1,47 @@
+package io.github.jan.supabase.realtime.event
+
+import io.github.jan.supabase.realtime.RealtimeChannel
+import io.github.jan.supabase.realtime.RealtimeMessage
+
+/**
+ * Interface for handling realtime events.
+ */
+internal sealed interface RealtimeEvent {
+
+ /**
+ * Handles the event.
+ * @param channel The channel the event was received on.
+ * @param message The message that was received.
+ */
+ suspend fun handle(channel: RealtimeChannel, message: RealtimeMessage)
+
+ /**
+ * Checks if the event applies to the message.
+ */
+ fun appliesTo(message: RealtimeMessage): Boolean
+
+ companion object {
+
+ private val EVENTS = setOf( // Kotlin doesn't provide a way to get all objects of a sealed interface outside the JVM, so we have to list them manually
+ RBroadcastEvent,
+ RCloseEvent,
+ RErrorEvent,
+ RPostgresChangesEvent,
+ RPostgresServerChangesEvent,
+ RPresenceStateEvent,
+ RPresenceDiffEvent,
+ RSystemEvent,
+ RTokenExpiredEvent,
+ RSystemReplyEvent
+ )
+
+ /**
+ * Resolves the event from a realtime message.
+ */
+ fun resolveEvent(realtimeMessage: RealtimeMessage): RealtimeEvent? {
+ return EVENTS.firstOrNull { it.appliesTo(realtimeMessage) }
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/Realtime/src/commonTest/kotlin/RealtimeChannelTest.kt b/Realtime/src/commonTest/kotlin/RealtimeChannelTest.kt
index 18e509219..5079b8475 100644
--- a/Realtime/src/commonTest/kotlin/RealtimeChannelTest.kt
+++ b/Realtime/src/commonTest/kotlin/RealtimeChannelTest.kt
@@ -1,7 +1,7 @@
import app.cash.turbine.test
-import io.github.jan.supabase.gotrue.Auth
-import io.github.jan.supabase.gotrue.auth
-import io.github.jan.supabase.gotrue.minimalSettings
+import io.github.jan.supabase.auth.Auth
+import io.github.jan.supabase.auth.auth
+import io.github.jan.supabase.auth.minimalSettings
import io.github.jan.supabase.postgrest.query.filter.FilterOperation
import io.github.jan.supabase.postgrest.query.filter.FilterOperator
import io.github.jan.supabase.realtime.CallbackManagerImpl
@@ -17,7 +17,6 @@ import io.github.jan.supabase.realtime.RealtimeMessage
import io.github.jan.supabase.realtime.broadcastFlow
import io.github.jan.supabase.realtime.channel
import io.github.jan.supabase.realtime.postgresChangeFlow
-import io.github.jan.supabase.realtime.presenceChangeFlow
import io.github.jan.supabase.realtime.realtime
import io.github.jan.supabase.testing.assertPathIs
import io.github.jan.supabase.testing.pathAfterVersion
diff --git a/Realtime/src/commonTest/kotlin/RealtimeMessageEventTypeTest.kt b/Realtime/src/commonTest/kotlin/RealtimeEventTest.kt
similarity index 69%
rename from Realtime/src/commonTest/kotlin/RealtimeMessageEventTypeTest.kt
rename to Realtime/src/commonTest/kotlin/RealtimeEventTest.kt
index fedffe9a2..872393dc3 100644
--- a/Realtime/src/commonTest/kotlin/RealtimeMessageEventTypeTest.kt
+++ b/Realtime/src/commonTest/kotlin/RealtimeEventTest.kt
@@ -1,11 +1,22 @@
import io.github.jan.supabase.realtime.RealtimeChannel
import io.github.jan.supabase.realtime.RealtimeMessage
+import io.github.jan.supabase.realtime.event.RBroadcastEvent
+import io.github.jan.supabase.realtime.event.RCloseEvent
+import io.github.jan.supabase.realtime.event.RErrorEvent
+import io.github.jan.supabase.realtime.event.RPostgresChangesEvent
+import io.github.jan.supabase.realtime.event.RPostgresServerChangesEvent
+import io.github.jan.supabase.realtime.event.RPresenceDiffEvent
+import io.github.jan.supabase.realtime.event.RPresenceStateEvent
+import io.github.jan.supabase.realtime.event.RSystemEvent
+import io.github.jan.supabase.realtime.event.RSystemReplyEvent
+import io.github.jan.supabase.realtime.event.RTokenExpiredEvent
+import io.github.jan.supabase.realtime.event.RealtimeEvent
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.put
import kotlin.test.Test
import kotlin.test.assertEquals
-class RealtimeMessageEventTypeTest {
+class RealtimeEventTest {
@Test
fun testSystemType() {
@@ -17,7 +28,7 @@ class RealtimeMessageEventTypeTest {
},
ref = ""
)
- assertEquals(RealtimeMessage.EventType.SYSTEM, message.eventType)
+ assertEquals(RSystemEvent, RealtimeEvent.resolveEvent(message))
}
@Test
@@ -30,7 +41,7 @@ class RealtimeMessageEventTypeTest {
},
ref = ""
)
- assertEquals(RealtimeMessage.EventType.SYSTEM_REPLY, message.eventType)
+ assertEquals(RSystemReplyEvent, RealtimeEvent.resolveEvent(message))
}
@Test
@@ -45,7 +56,7 @@ class RealtimeMessageEventTypeTest {
},
ref = ""
)
- assertEquals(RealtimeMessage.EventType.POSTGRES_SERVER_CHANGES, message.eventType)
+ assertEquals(RPostgresServerChangesEvent, RealtimeEvent.resolveEvent(message))
}
@Test
@@ -58,7 +69,7 @@ class RealtimeMessageEventTypeTest {
},
ref = ""
)
- assertEquals(RealtimeMessage.EventType.POSTGRES_CHANGES, message.eventType)
+ assertEquals(RPostgresChangesEvent, RealtimeEvent.resolveEvent(message))
}
@Test
@@ -71,7 +82,7 @@ class RealtimeMessageEventTypeTest {
},
ref = ""
)
- assertEquals(RealtimeMessage.EventType.BROADCAST, message.eventType)
+ assertEquals(RBroadcastEvent, RealtimeEvent.resolveEvent(message))
}
@Test
@@ -84,7 +95,7 @@ class RealtimeMessageEventTypeTest {
},
ref = ""
)
- assertEquals(RealtimeMessage.EventType.CLOSE, message.eventType)
+ assertEquals(RCloseEvent, RealtimeEvent.resolveEvent(message))
}
@Test
@@ -97,7 +108,7 @@ class RealtimeMessageEventTypeTest {
},
ref = ""
)
- assertEquals(RealtimeMessage.EventType.ERROR, message.eventType)
+ assertEquals(RErrorEvent, RealtimeEvent.resolveEvent(message))
}
@Test
@@ -110,7 +121,7 @@ class RealtimeMessageEventTypeTest {
},
ref = ""
)
- assertEquals(RealtimeMessage.EventType.PRESENCE_DIFF, message.eventType)
+ assertEquals(RPresenceDiffEvent, RealtimeEvent.resolveEvent(message))
}
@Test
@@ -123,7 +134,7 @@ class RealtimeMessageEventTypeTest {
},
ref = ""
)
- assertEquals(RealtimeMessage.EventType.PRESENCE_STATE, message.eventType)
+ assertEquals(RPresenceStateEvent, RealtimeEvent.resolveEvent(message))
}
@Test
@@ -136,7 +147,7 @@ class RealtimeMessageEventTypeTest {
},
ref = ""
)
- assertEquals(RealtimeMessage.EventType.TOKEN_EXPIRED, message.eventType)
+ assertEquals(RTokenExpiredEvent, RealtimeEvent.resolveEvent(message))
}
}
\ No newline at end of file
diff --git a/Realtime/src/commonTest/kotlin/RealtimeTestUtils.kt b/Realtime/src/commonTest/kotlin/RealtimeTestUtils.kt
index 957d1f694..34edb30fb 100644
--- a/Realtime/src/commonTest/kotlin/RealtimeTestUtils.kt
+++ b/Realtime/src/commonTest/kotlin/RealtimeTestUtils.kt
@@ -1,5 +1,5 @@
-import io.github.jan.supabase.gotrue.Auth
-import io.github.jan.supabase.gotrue.user.UserSession
+import io.github.jan.supabase.auth.Auth
+import io.github.jan.supabase.auth.user.UserSession
import io.github.jan.supabase.putJsonObject
import io.github.jan.supabase.realtime.Presence
import io.github.jan.supabase.realtime.RealtimeChannel.Companion.CHANNEL_EVENT_PRESENCE_DIFF
diff --git a/Storage/README.md b/Storage/README.md
index 9ca598dd4..5dad33b1d 100644
--- a/Storage/README.md
+++ b/Storage/README.md
@@ -4,9 +4,9 @@ Extends Supabase-kt with a multiplatform Storage client.
Supported targets:
-| Target | **JVM** | **Android** | **JS** | **iOS** | **tvOS** | **watchOS** | **macOS** | **Windows** | **Linux** |
-| ------ | ------- | ----------- | ------ | ------- | -------- | ----------- | --------- | ----------- | --------- |
-| | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
+| Target | **JVM** | **Android** | **JS** | **Wasm** | **Apple** | **Windows** | **Linux** |
+|--------|---------|-------------|--------|----------|-----------|-------------|-----------|
+| Status | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
> Linux has no support for persistent resumable upload url caching.
@@ -14,15 +14,19 @@ Supported targets:
In-depth Kotlin targets
-**iOS:** iosArm64, iosSimulatorArm64, iosX64
-
**JS**: Browser, NodeJS
-**tvOS**: tvosArm64, tvosX64, tvosSimulatorArm64
+**Wasm**: wasm-js
+
+**Apple:**
+
+- iOS: iosArm64, iosSimulatorArm64, iosX64
+
+- tvOS: tvosArm64, tvosX64, tvosSimulatorArm64
-**watchOS**: watchosArm64, watchosX64, watchosSimulatorArm64
+- watchOS: watchosArm64, watchosX64, watchosSimulatorArm64
-**MacOS**: macosX64, macosArm64
+- MacOS: macosX64, macosArm64
**Windows**: mingwX64
diff --git a/Storage/build.gradle.kts b/Storage/build.gradle.kts
index f609bd2ec..af6f30875 100644
--- a/Storage/build.gradle.kts
+++ b/Storage/build.gradle.kts
@@ -22,7 +22,7 @@ kotlin {
sourceSets {
val commonMain by getting {
dependencies {
- addModules(SupabaseModule.GOTRUE)
+ addModules(SupabaseModule.AUTH)
}
}
val commonTest by getting {
diff --git a/Storage/src/androidAndJvmMain/kotlin/io/github/jan/supabase/storage/JvmUtils.kt b/Storage/src/androidAndJvmMain/kotlin/io/github/jan/supabase/storage/JvmUtils.kt
index 2586f13d5..213e319fa 100644
--- a/Storage/src/androidAndJvmMain/kotlin/io/github/jan/supabase/storage/JvmUtils.kt
+++ b/Storage/src/androidAndJvmMain/kotlin/io/github/jan/supabase/storage/JvmUtils.kt
@@ -11,163 +11,170 @@ import kotlin.io.path.writeBytes
* Uploads a file in [BucketApi.bucketId] under [path]
* @param path The path to upload the file to
* @param file The file to upload
- * @param upsert Whether to overwrite an existing file
+ * @param options Additional options for the upload
* @return the key to the uploaded file
*/
-suspend fun BucketApi.upload(path: String, file: File, upsert: Boolean = false) = upload(path, UploadData(file.readChannel(), file.length()), upsert)
+suspend fun BucketApi.upload(path: String, file: File, options: UploadOptionBuilder.() -> Unit = {}) = upload(path, UploadData(file.readChannel(), file.length()), options)
/**
* Uploads a file in [BucketApi.bucketId] under [path]
* @param path The path to upload the file to
* @param file The file to upload
- * @param upsert Whether to overwrite an existing file
+ * @param options Additional options for the upload
* @return A flow that emits the upload progress and at last the key to the uploaded file
*/
-fun BucketApi.uploadAsFlow(path: String, file: File, upsert: Boolean = false) = uploadAsFlow(path, UploadData(file.readChannel(), file.length()), upsert)
+fun BucketApi.uploadAsFlow(path: String, file: File, options: UploadOptionBuilder.() -> Unit = {}) = uploadAsFlow(path, UploadData(file.readChannel(), file.length()), options)
/**
* Uploads a file in [BucketApi.bucketId] under [path]
* @param path The path to upload the file to
* @param file The file to upload
- * @param upsert Whether to overwrite an existing file
+ * @param options Additional options for the upload
* @return the key to the uploaded file
*/
-suspend fun BucketApi.upload(path: String, file: Path, upsert: Boolean = false) = upload(path, UploadData(file.readChannel(), file.fileSize()), upsert)
+suspend fun BucketApi.upload(path: String, file: Path, options: UploadOptionBuilder.() -> Unit = {}) = upload(path, UploadData(file.readChannel(), file.fileSize()), options)
/**
* Uploads a file in [BucketApi.bucketId] under [path]
* @param path The path to upload the file to
* @param file The file to upload
- * @param upsert Whether to overwrite an existing file
+ * @param options Additional options for the upload
* @return A flow that emits the upload progress and at last the key to the uploaded file
*/
-fun BucketApi.uploadAsFlow(path: String, file: Path, upsert: Boolean = false) = uploadAsFlow(path, UploadData(file.readChannel(), file.fileSize()), upsert)
+fun BucketApi.uploadAsFlow(path: String, file: Path, options: UploadOptionBuilder.() -> Unit = {}) = uploadAsFlow(path, UploadData(file.readChannel(), file.fileSize()), options)
/**
* Uploads a file in [BucketApi.bucketId] under [path] using a presigned url
* @param path The path to upload the file to
- * @param token The presigned url token
+ * @param token The pre-signed url token
* @param file The file to upload
- * @param upsert Whether to overwrite an existing file
+ * @param options Additional options for the upload
* @return the key to the uploaded file
*/
-suspend fun BucketApi.uploadToSignedUrl(path: String, token: String, file: File, upsert: Boolean = false) = uploadToSignedUrl(path, token, UploadData(file.readChannel(), file.length()), upsert)
+suspend fun BucketApi.uploadToSignedUrl(path: String, token: String, file: File, options: UploadOptionBuilder.() -> Unit = {}) = uploadToSignedUrl(path, token, UploadData(file.readChannel(), file.length()), options)
/**
* Uploads a file in [BucketApi.bucketId] under [path] using a presigned url
* @param path The path to upload the file to
- * @param token The presigned url token
+ * @param token The pre-signed url token
* @param file The file to upload
- * @param upsert Whether to overwrite an existing file
+ * @param options Additional options for the upload
* @return A flow that emits the upload progress and at last the key to the uploaded file
*/
-fun BucketApi.uploadToSignedUrlAsFlow(path: String, token: String, file: File, upsert: Boolean = false) = uploadToSignedUrlAsFlow(path, token, UploadData(file.readChannel(), file.length()), upsert)
+fun BucketApi.uploadToSignedUrlAsFlow(path: String, token: String, file: File, options: UploadOptionBuilder.() -> Unit = {}) = uploadToSignedUrlAsFlow(path, token, UploadData(file.readChannel(), file.length()), options)
/**
* Uploads a file in [BucketApi.bucketId] under [path] using a presigned url
* @param path The path to upload the file to
- * @param token The presigned url token
+ * @param token The pre-signed url token
* @param file The file to upload
- * @param upsert Whether to overwrite an existing file
+ * @param options Additional options for the upload
*/
-suspend fun BucketApi.uploadToSignedUrl(path: String, token: String, file: Path, upsert: Boolean = false) = uploadToSignedUrl(path, token, UploadData(file.readChannel(), file.fileSize()), upsert)
+suspend fun BucketApi.uploadToSignedUrl(path: String, token: String, file: Path, options: UploadOptionBuilder.() -> Unit = {}) = uploadToSignedUrl(path, token, UploadData(file.readChannel(), file.fileSize()), options)
/**
* Uploads a file in [BucketApi.bucketId] under [path] using a presigned url
* @param path The path to upload the file to
- * @param token The presigned url token
+ * @param token The pre-signed url token
* @param file The file to upload
- * @param upsert Whether to overwrite an existing file
+ * @param options Additional options for the upload
* @return A flow that emits the upload progress and at last the key to the uploaded file
*/
-fun BucketApi.uploadToSignedUrlAsFlow(path: String, token: String, file: Path, upsert: Boolean = false) = uploadToSignedUrlAsFlow(path, token, UploadData(file.readChannel(), file.fileSize()), upsert)
+fun BucketApi.uploadToSignedUrlAsFlow(path: String, token: String, file: Path, options: UploadOptionBuilder.() -> Unit = {}) = uploadToSignedUrlAsFlow(path, token, UploadData(file.readChannel(), file.fileSize()), options)
/**
* Updates a file in [BucketApi.bucketId] under [path]
* @param path The path to be updated
* @param file The new file
- * @param upsert Whether to overwrite an existing file
+ * @param options Additional options for the upload
*/
-suspend fun BucketApi.update(path: String, file: Path, upsert: Boolean = false) = update(path, UploadData(file.readChannel(), file.fileSize()), upsert)
+suspend fun BucketApi.update(path: String, file: Path, options: UploadOptionBuilder.() -> Unit = {}) = update(path, UploadData(file.readChannel(), file.fileSize()), options)
/**
* Updates a file in [BucketApi.bucketId] under [path]
* @param path The path to be updated
* @param file The new file
- * @param upsert Whether to overwrite an existing file
+ * @param options Additional options for the upload
* @return A flow that emits the upload progress and at last the key to the uploaded file
*/
-fun BucketApi.updateAsFlow(path: String, file: Path, upsert: Boolean = false) = updateAsFlow(path, UploadData(file.readChannel(), file.fileSize()), upsert)
+fun BucketApi.updateAsFlow(path: String, file: Path, options: UploadOptionBuilder.() -> Unit = {}) = updateAsFlow(path, UploadData(file.readChannel(), file.fileSize()), options)
/**
* Updates a file in [BucketApi.bucketId] under [path]
* @param path The path to be updated
* @param file The new file
- * @param upsert Whether to overwrite an existing file
+ * @param options Additional options for the upload
*/
-suspend fun BucketApi.update(path: String, file: File, upsert: Boolean = false) = update(path, UploadData(file.readChannel(), file.length()), upsert)
+suspend fun BucketApi.update(path: String, file: File, options: UploadOptionBuilder.() -> Unit = {}) = update(path, UploadData(file.readChannel(), file.length()), options)
/**
* Updates a file in [BucketApi.bucketId] under [path]
* @param path The path to be updated
* @param file The new file
- * @param upsert Whether to overwrite an existing file
+ * @param options Additional options for the upload
* @return A flow that emits the upload progress and at last the key to the uploaded file
*/
-fun BucketApi.updateAsFlow(path: String, file: File, upsert: Boolean = false) = updateAsFlow(path, UploadData(file.readChannel(), file.length()), upsert)
+fun BucketApi.updateAsFlow(path: String, file: File, options: UploadOptionBuilder.() -> Unit = {}) = updateAsFlow(path, UploadData(file.readChannel(), file.length()), options)
/**
* Downloads a file from [BucketApi.bucketId] under [path] and saves it to [file]
* @param path The path to download the file from
* @param file The file to save the data to
+ * @param options Additional options for the download
*/
-suspend fun BucketApi.downloadAuthenticatedTo(path: String, file: File, transform: ImageTransformation.() -> Unit = {}) = downloadAuthenticated(path, file.writeChannel(), transform)
+suspend fun BucketApi.downloadAuthenticatedTo(path: String, file: File, options: DownloadOptionBuilder.() -> Unit = {}) = downloadAuthenticated(path, file.writeChannel(), options)
/**
* Downloads a file from [BucketApi.bucketId] under [path] and saves it to [file]
* @param path The path to download the file from
* @param file The file to save the data to
+ * @param options Additional options for the download
* @return A flow that emits the download progress and at last the key to the downloaded file
*/
-fun BucketApi.downloadAuthenticatedToAsFlow(path: String, file: File, transform: ImageTransformation.() -> Unit = {}) = downloadAuthenticatedAsFlow(path, file.writeChannel(), transform)
+fun BucketApi.downloadAuthenticatedToAsFlow(path: String, file: File, options: DownloadOptionBuilder.() -> Unit = {}) = downloadAuthenticatedAsFlow(path, file.writeChannel(), options)
/**
* Downloads a file from [BucketApi.bucketId] under [path] and saves it to [file]
* @param path The path to download the file from
* @param file The file to save the data to
+ * @param options Additional options for the download
*/
-suspend fun BucketApi.downloadAuthenticatedTo(path: String, file: Path, transform: ImageTransformation.() -> Unit = {}) = downloadAuthenticated(path, file.toFile().writeChannel(), transform)
+suspend fun BucketApi.downloadAuthenticatedTo(path: String, file: Path, options: DownloadOptionBuilder.() -> Unit = {}) = downloadAuthenticated(path, file.toFile().writeChannel(), options)
/**
* Downloads a file from [BucketApi.bucketId] under [path] and saves it to [file]
* @param path The path to download the file from
* @param file The file to save the data to
+ * @param options Additional options for the download
* @return A flow that emits the download progress and at last the key to the downloaded file
*/
-fun BucketApi.downloadAuthenticatedToAsFlow(path: String, file: Path, transform: ImageTransformation.() -> Unit = {}) = downloadAuthenticatedAsFlow(path, file.toFile().writeChannel(), transform)
+fun BucketApi.downloadAuthenticatedToAsFlow(path: String, file: Path, options: DownloadOptionBuilder.() -> Unit = {}) = downloadAuthenticatedAsFlow(path, file.toFile().writeChannel(), options)
/**
* Downloads a file from [BucketApi.bucketId] under [path] and saves it to [file]
* @param path The path to download the file from
* @param file The file to save the data to
+ * @param options Additional options for the download
*/
-suspend fun BucketApi.downloadPublicTo(path: String, file: File, transform: ImageTransformation.() -> Unit = {}) = downloadPublic(path, file.writeChannel(), transform)
+suspend fun BucketApi.downloadPublicTo(path: String, file: File, options: DownloadOptionBuilder.() -> Unit = {}) = downloadPublic(path, file.writeChannel(), options)
/**
* Downloads a file from [BucketApi.bucketId] under [path] and saves it to [file]
* @param path The path to download the file from
* @param file The file to save the data to
+ * @param options Additional options for the download
* @return A flow that emits the download progress and at last the key to the downloaded file
*/
-fun BucketApi.downloadPublicToAsFlow(path: String, file: File, transform: ImageTransformation.() -> Unit = {}) = downloadPublicAsFlow(path, file.writeChannel(), transform)
+fun BucketApi.downloadPublicToAsFlow(path: String, file: File, options: DownloadOptionBuilder.() -> Unit = {}) = downloadPublicAsFlow(path, file.writeChannel(), options)
/**
* Downloads a file from [BucketApi.bucketId] under [path] and saves it to [file]
* @param path The path to download the file from
* @param file The file to save the data to
+ * @param options Additional options for the download
*/
-suspend fun BucketApi.downloadPublicTo(path: String, file: Path, transform: ImageTransformation.() -> Unit = {}) {
- val bytes = downloadPublic(path, transform)
+suspend fun BucketApi.downloadPublicTo(path: String, file: Path, options: DownloadOptionBuilder.() -> Unit = {}) {
+ val bytes = downloadPublic(path, options)
file.writeBytes(bytes)
}
@@ -175,6 +182,7 @@ suspend fun BucketApi.downloadPublicTo(path: String, file: Path, transform: Imag
* Downloads a file from [BucketApi.bucketId] under [path] and saves it to [file]
* @param path The path to download the file from
* @param file The file to save the data to
+ * @param options Additional options for the download
* @return A flow that emits the download progress and at last the key to the downloaded file
*/
-fun BucketApi.downloadPublicToAsFlow(path: String, file: Path, transform: ImageTransformation.() -> Unit = {}) = downloadPublicAsFlow(path, file.toFile().writeChannel(), transform)
\ No newline at end of file
+fun BucketApi.downloadPublicToAsFlow(path: String, file: Path, options: DownloadOptionBuilder.() -> Unit = {}) = downloadPublicAsFlow(path, file.toFile().writeChannel(), options)
\ No newline at end of file
diff --git a/Storage/src/androidAndJvmMain/kotlin/io/github/jan/supabase/storage/ResumableUtils.kt b/Storage/src/androidAndJvmMain/kotlin/io/github/jan/supabase/storage/ResumableUtils.kt
index 71ef1bbb7..c39d054ac 100644
--- a/Storage/src/androidAndJvmMain/kotlin/io/github/jan/supabase/storage/ResumableUtils.kt
+++ b/Storage/src/androidAndJvmMain/kotlin/io/github/jan/supabase/storage/ResumableUtils.kt
@@ -3,6 +3,7 @@ package io.github.jan.supabase.storage
import io.github.jan.supabase.storage.resumable.Fingerprint
import io.github.jan.supabase.storage.resumable.ResumableClient
import io.ktor.util.cio.readChannel
+import io.ktor.utils.io.discard
import java.io.File
import java.nio.file.Path
import kotlin.io.path.absolutePathString
diff --git a/Storage/src/androidMain/kotlin/io/github/jan/supabase/storage/AndroidUtils.kt b/Storage/src/androidMain/kotlin/io/github/jan/supabase/storage/AndroidUtils.kt
index 48f883f99..e30c50706 100644
--- a/Storage/src/androidMain/kotlin/io/github/jan/supabase/storage/AndroidUtils.kt
+++ b/Storage/src/androidMain/kotlin/io/github/jan/supabase/storage/AndroidUtils.kt
@@ -9,56 +9,56 @@ import io.ktor.utils.io.jvm.javaio.toByteReadChannel
* Uploads a file in [BucketApi.bucketId] under [path]
* @param path The path to upload the file to
* @param uri The uri to upload
- * @param upsert Whether to overwrite an existing file
+ * @param options Additional options for the upload
* @return the key to the updated file
*/
-suspend fun BucketApi.upload(path: String, uri: Uri, upsert: Boolean = false) = upload(path, UploadData(uri.readChannel(), uri.contentSize), upsert)
+suspend fun BucketApi.upload(path: String, uri: Uri, options: UploadOptionBuilder.() -> Unit = {}) = upload(path, UploadData(uri.readChannel(), uri.contentSize), options)
/**
* Uploads a file in [BucketApi.bucketId] under [path]
* @param path The path to upload the file to
* @param uri The uri to upload
- * @param upsert Whether to overwrite an existing file
+ * @param options Additional options for the upload
* @return A flow that emits the upload progress and at last the key to the updated file
*/
-fun BucketApi.uploadAsFlow(path: String, uri: Uri, upsert: Boolean = false) = uploadAsFlow(path, UploadData(uri.readChannel(), uri.contentSize), upsert)
+fun BucketApi.uploadAsFlow(path: String, uri: Uri, options: UploadOptionBuilder.() -> Unit = {}) = uploadAsFlow(path, UploadData(uri.readChannel(), uri.contentSize), options)
/**
* Uploads a file in [BucketApi.bucketId] under [path] using a presigned url
* @param path The path to upload the file to
- * @param token The presigned url token
+ * @param token The pre-signed url token
* @param uri The uri to upload
* @return the key to the updated file
*/
-suspend fun BucketApi.uploadToSignedUrl(path: String, token: String, uri: Uri, upsert: Boolean = false) = uploadToSignedUrl(path, token, UploadData(uri.readChannel(), uri.contentSize), upsert)
+suspend fun BucketApi.uploadToSignedUrl(path: String, token: String, uri: Uri, options: UploadOptionBuilder.() -> Unit = {}) = uploadToSignedUrl(path, token, UploadData(uri.readChannel(), uri.contentSize), options)
/**
* Uploads a file in [BucketApi.bucketId] under [path] using a presigned url
* @param path The path to upload the file to
- * @param token The presigned url token
+ * @param token The pre-signed url token
* @param uri The uri to upload
- * @param upsert Whether to overwrite an existing file
+ * @param options Additional options for the upload
* @return A flow that emits the upload progress and at last the key to the updated file
*/
-fun BucketApi.uploadToSignedUrlAsFlow(path: String, token: String, uri: Uri, upsert: Boolean = false) = uploadToSignedUrlAsFlow(path, token, UploadData(uri.readChannel(), uri.contentSize), upsert)
+fun BucketApi.uploadToSignedUrlAsFlow(path: String, token: String, uri: Uri, options: UploadOptionBuilder.() -> Unit = {}) = uploadToSignedUrlAsFlow(path, token, UploadData(uri.readChannel(), uri.contentSize), options)
/**
* Updates a file in [BucketApi.bucketId] under [path]
* @param path The path to update the file to
* @param uri The uri to update
- * @param upsert Whether to overwrite an existing file
+ * @param options Additional options for the upload
* @return the key to the updated file
*/
-suspend fun BucketApi.update(path: String, uri: Uri, upsert: Boolean = false) = update(path, UploadData(uri.readChannel(), uri.contentSize), upsert)
+suspend fun BucketApi.update(path: String, uri: Uri, options: UploadOptionBuilder.() -> Unit = {}) = update(path, UploadData(uri.readChannel(), uri.contentSize), options)
/**
* Updates a file in [BucketApi.bucketId] under [path]
* @param path The path to update the file to
* @param uri The uri to update
- * @param upsert Whether to overwrite an existing file
+ * @param options Additional options for the upload
* @return A flow that emits the upload progress and at last the key to the updated file
*/
-fun BucketApi.updateAsFlow(path: String, uri: Uri, upsert: Boolean = false) = updateAsFlow(path, UploadData(uri.readChannel(), uri.contentSize), upsert)
+fun BucketApi.updateAsFlow(path: String, uri: Uri, options: UploadOptionBuilder.() -> Unit = {}) = updateAsFlow(path, UploadData(uri.readChannel(), uri.contentSize), options)
@SuppressLint("Recycle") //toByteReadChannel closes the input stream automatically
private fun Uri.readChannel(): ByteReadChannel {
diff --git a/Storage/src/androidMain/kotlin/io/github/jan/supabase/storage/ResumableAndroidUtils.kt b/Storage/src/androidMain/kotlin/io/github/jan/supabase/storage/ResumableAndroidUtils.kt
index 000091b68..85651b333 100644
--- a/Storage/src/androidMain/kotlin/io/github/jan/supabase/storage/ResumableAndroidUtils.kt
+++ b/Storage/src/androidMain/kotlin/io/github/jan/supabase/storage/ResumableAndroidUtils.kt
@@ -5,6 +5,7 @@ import android.content.res.AssetFileDescriptor
import android.net.Uri
import io.github.jan.supabase.storage.resumable.ResumableClient
import io.ktor.utils.io.ByteReadChannel
+import io.ktor.utils.io.discard
import io.ktor.utils.io.jvm.javaio.toByteReadChannel
/**
diff --git a/Storage/src/commonMain/kotlin/io/github/jan/supabase/storage/BucketApi.kt b/Storage/src/commonMain/kotlin/io/github/jan/supabase/storage/BucketApi.kt
index a4c7da748..5003106c9 100644
--- a/Storage/src/commonMain/kotlin/io/github/jan/supabase/storage/BucketApi.kt
+++ b/Storage/src/commonMain/kotlin/io/github/jan/supabase/storage/BucketApi.kt
@@ -1,9 +1,9 @@
package io.github.jan.supabase.storage
import io.github.jan.supabase.SupabaseClient
+import io.github.jan.supabase.auth.resolveAccessToken
import io.github.jan.supabase.exceptions.HttpRequestException
import io.github.jan.supabase.exceptions.RestException
-import io.github.jan.supabase.gotrue.resolveAccessToken
import io.github.jan.supabase.storage.resumable.ResumableClient
import io.ktor.client.plugins.HttpRequestTimeoutException
import io.ktor.utils.io.ByteReadChannel
@@ -34,43 +34,47 @@ sealed interface BucketApi {
* Uploads a file in [bucketId] under [path]
* @param path The path to upload the file to
* @param data The data to upload
- * @param upsert Whether to overwrite an existing file
+ * @param options Additional options for the upload
* @return the key to the uploaded file
* @throws IllegalArgumentException if data to upload is empty
* @throws RestException or one of its subclasses if receiving an error response
* @throws HttpRequestTimeoutException if the request timed out
* @throws HttpRequestException on network related issues
*/
- suspend fun upload(path: String, data: ByteArray, upsert: Boolean = false): FileUploadResponse {
+ suspend fun upload(path: String, data: ByteArray, options: UploadOptionBuilder.() -> Unit = {}): FileUploadResponse {
require(data.isNotEmpty()) { "The data to upload should not be empty" }
- return upload(path, UploadData(ByteReadChannel(data), data.size.toLong()), upsert)
+ return upload(path, UploadData(ByteReadChannel(data), data.size.toLong()), options)
}
/**
* Uploads a file in [bucketId] under [path]
* @param path The path to upload the file to
* @param data The data to upload
- * @param upsert Whether to overwrite an existing file
+ * @param options Additional options for the upload
* @return the key to the uploaded file
* @throws RestException or one of its subclasses if receiving an error response
* @throws HttpRequestTimeoutException if the request timed out
* @throws HttpRequestException on network related issues
*/
- suspend fun upload(path: String, data: UploadData, upsert: Boolean = false): FileUploadResponse
+ suspend fun upload(path: String, data: UploadData, options: UploadOptionBuilder.() -> Unit = {}): FileUploadResponse
/**
- * Uploads a file in [bucketId] under [path] using a presigned url
+ * Uploads a file in [bucketId] under [path] using a pre-signed url
* @param path The path to upload the file to
- * @param token The presigned url token
+ * @param token The pre-signed url token
* @param data The data to upload
- * @param upsert Whether to overwrite an existing file
+ * @param options Additional options for the upload
* @return the key of the uploaded file
* @throws IllegalArgumentException if data to upload is empty
*/
- suspend fun uploadToSignedUrl(path: String, token: String, data: ByteArray, upsert: Boolean = false
+ suspend fun uploadToSignedUrl(
+ path: String,
+ token: String,
+ data: ByteArray,
+ options: UploadOptionBuilder.() -> Unit = {}
): FileUploadResponse {
require(data.isNotEmpty()) { "The data to upload should not be empty" }
- return uploadToSignedUrl(path, token, UploadData(ByteReadChannel(data), data.size.toLong()), upsert)
+ return uploadToSignedUrl(path, token, UploadData(ByteReadChannel(data), data.size.toLong()), options)
}
/**
@@ -78,42 +82,42 @@ sealed interface BucketApi {
* @param path The path to upload the file to
* @param token The presigned url token
* @param data The data to upload
- * @param upsert Whether to overwrite an existing file
+ * @param options Additional options for the upload
* @return the key of the uploaded file
* @throws RestException or one of its subclasses if receiving an error response
* @throws HttpRequestTimeoutException if the request timed out
* @throws HttpRequestException on network related issues
* @throws HttpRequestException on network related issues
*/
- suspend fun uploadToSignedUrl(path: String, token: String, data: UploadData, upsert: Boolean = false): FileUploadResponse
+ suspend fun uploadToSignedUrl(path: String, token: String, data: UploadData, options: UploadOptionBuilder.() -> Unit = {}): FileUploadResponse
/**
* Updates a file in [bucketId] under [path]
* @param path The path to update the file to
* @param data The new data
- * @param upsert Whether to overwrite an existing file
+ * @param options Additional options for the upload
* @return the key to the updated file
* @throws IllegalArgumentException if data to upload is empty
* @throws RestException or one of its subclasses if receiving an error response
* @throws HttpRequestTimeoutException if the request timed out
* @throws HttpRequestException on network related issues
*/
- suspend fun update(path: String, data: ByteArray, upsert: Boolean = false): FileUploadResponse {
+ suspend fun update(path: String, data: ByteArray, options: UploadOptionBuilder.() -> Unit = {}): FileUploadResponse {
require(data.isNotEmpty()) { "The data to upload should not be empty" }
- return update(path, UploadData(ByteReadChannel(data), data.size.toLong()), upsert)
+ return update(path, UploadData(ByteReadChannel(data), data.size.toLong()), options)
}
/**
* Updates a file in [bucketId] under [path]
* @param path The path to update the file to
* @param data The new data
- * @param upsert Whether to overwrite an existing file
+ * @param options Additional options for the upload
* @return the key to the updated file
* @throws RestException or one of its subclasses if receiving an error response
* @throws HttpRequestTimeoutException if the request timed out
* @throws HttpRequestException on network related issues
*/
- suspend fun update(path: String, data: UploadData, upsert: Boolean = false): FileUploadResponse
+ suspend fun update(path: String, data: UploadData, options: UploadOptionBuilder.() -> Unit = {}): FileUploadResponse
/**
* Deletes all files in [bucketId] with in [paths]
@@ -199,51 +203,50 @@ sealed interface BucketApi {
/**
* Downloads a file from [bucketId] under [path]
* @param path The path to download
- * @param transform The transformation to apply to the image
+ * @param options Additional options for the download
* @return The file as a byte array
* @throws RestException or one of its subclasses if receiving an error response
* @throws HttpRequestTimeoutException if the request timed out
* @throws HttpRequestException on network related issues
*/
- suspend fun downloadAuthenticated(path: String, transform: ImageTransformation.() -> Unit = {}): ByteArray
+ suspend fun downloadAuthenticated(path: String, options: DownloadOptionBuilder.() -> Unit = {}): ByteArray
/**
* Downloads a file from [bucketId] under [path]
* @param path The path to download
* @param channel The channel to write the data to
- * @param transform The transformation to apply to the image
+ * @param options Additional options for the download
* @throws RestException or one of its subclasses if receiving an error response
* @throws HttpRequestTimeoutException if the request timed out
* @throws HttpRequestException on network related issues
*/
- suspend fun downloadAuthenticated(path: String, channel: ByteWriteChannel, transform: ImageTransformation.() -> Unit = {})
+ suspend fun downloadAuthenticated(path: String, channel: ByteWriteChannel, options: DownloadOptionBuilder.() -> Unit = {})
/**
* Downloads a file from [bucketId] under [path] using the public url
* @param path The path to download
- * @param transform The transformation to apply to the image
+ * @param options Additional options for the download
* @return The file as a byte array
* @throws RestException or one of its subclasses if receiving an error response
* @throws HttpRequestTimeoutException if the request timed out
* @throws HttpRequestException on network related issues
*/
- suspend fun downloadPublic(path: String, transform: ImageTransformation.() -> Unit = {}): ByteArray
+ suspend fun downloadPublic(path: String, options: DownloadOptionBuilder.() -> Unit = {}): ByteArray
/**
* Downloads a file from [bucketId] under [path] using the public url
* @param path The path to download
* @param channel The channel to write the data to
- * @param transform The transformation to apply to the image
+ * @param options Additional options for the download
* @throws RestException or one of its subclasses if receiving an error response
* @throws HttpRequestTimeoutException if the request timed out
* @throws HttpRequestException on network related issues
*/
- suspend fun downloadPublic(path: String, channel: ByteWriteChannel, transform: ImageTransformation.() -> Unit = {})
+ suspend fun downloadPublic(path: String, channel: ByteWriteChannel, options: DownloadOptionBuilder.() -> Unit = {})
/**
- * Searches for buckets with the given [prefix] and [filter]
- * @return The filtered buckets
+ * Searches for files with the given [prefix] and [filter]
* @throws RestException or one of its subclasses if receiving an error response
* @throws HttpRequestTimeoutException if the request timed out
* @throws HttpRequestException on network related issues
@@ -251,7 +254,7 @@ sealed interface BucketApi {
suspend fun list(
prefix: String = "",
filter: BucketListFilter.() -> Unit = {}
- ): List
+ ): List
/**
* Changes the bucket's public status to [public]
diff --git a/Storage/src/commonMain/kotlin/io/github/jan/supabase/storage/BucketApiImpl.kt b/Storage/src/commonMain/kotlin/io/github/jan/supabase/storage/BucketApiImpl.kt
index 4d664684c..986f2582d 100644
--- a/Storage/src/commonMain/kotlin/io/github/jan/supabase/storage/BucketApiImpl.kt
+++ b/Storage/src/commonMain/kotlin/io/github/jan/supabase/storage/BucketApiImpl.kt
@@ -8,7 +8,6 @@ import io.github.jan.supabase.storage.resumable.ResumableClientImpl
import io.ktor.client.call.body
import io.ktor.client.request.HttpRequestBuilder
import io.ktor.client.request.header
-import io.ktor.client.request.parameter
import io.ktor.client.request.setBody
import io.ktor.client.request.url
import io.ktor.client.statement.bodyAsChannel
@@ -20,7 +19,6 @@ import io.ktor.http.content.OutgoingContent
import io.ktor.http.defaultForFilePath
import io.ktor.utils.io.ByteReadChannel
import io.ktor.utils.io.ByteWriteChannel
-import io.ktor.utils.io.close
import io.ktor.utils.io.copyTo
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.add
@@ -37,18 +35,24 @@ internal class BucketApiImpl(override val bucketId: String, val storage: Storage
override val resumable = ResumableClientImpl(this, resumableCache)
- override suspend fun update(path: String, data: UploadData, upsert: Boolean): FileUploadResponse =
+ override suspend fun update(
+ path: String,
+ data: UploadData,
+ options: UploadOptionBuilder.() -> Unit
+ ): FileUploadResponse =
uploadOrUpdate(
- HttpMethod.Put, bucketId, path, data, upsert
+ HttpMethod.Put, defaultUploadUrl(path), data, options
)
override suspend fun uploadToSignedUrl(
path: String,
token: String,
data: UploadData,
- upsert: Boolean
+ options: UploadOptionBuilder.() -> Unit
): FileUploadResponse {
- return uploadToSignedUrl(path, token, data, upsert) {}
+ return uploadOrUpdate(
+ HttpMethod.Put, uploadToSignedUrlUrl(path, token), data, options
+ )
}
override suspend fun createSignedUploadUrl(path: String): UploadSignedUrl {
@@ -64,9 +68,13 @@ internal class BucketApiImpl(override val bucketId: String, val storage: Storage
)
}
- override suspend fun upload(path: String, data: UploadData, upsert: Boolean): FileUploadResponse =
+ override suspend fun upload(
+ path: String,
+ data: UploadData,
+ options: UploadOptionBuilder.() -> Unit
+ ): FileUploadResponse =
uploadOrUpdate(
- HttpMethod.Post, bucketId, path, data, upsert
+ HttpMethod.Post, defaultUploadUrl(path), data, options
)
override suspend fun delete(paths: Collection) {
@@ -128,72 +136,74 @@ internal class BucketApiImpl(override val bucketId: String, val storage: Storage
override suspend fun downloadAuthenticated(
path: String,
- transform: ImageTransformation.() -> Unit
- ): ByteArray {
- return storage.api.rawRequest {
- prepareDownloadRequest(path, false, transform)
- }.body()
- }
+ options: DownloadOptionBuilder.() -> Unit
+ ): ByteArray = normalDownloadRequest(path, false, options)
override suspend fun downloadPublic(
path: String,
- transform: ImageTransformation.() -> Unit
+ options: DownloadOptionBuilder.() -> Unit
+ ): ByteArray = normalDownloadRequest(path, true, options)
+
+ private suspend fun normalDownloadRequest(
+ path: String,
+ public: Boolean,
+ options: DownloadOptionBuilder.() -> Unit
): ByteArray {
+ val downloadOptions = DownloadOptionBuilder().apply(options)
return storage.api.rawRequest {
- prepareDownloadRequest(path, true, transform)
+ prepareDownloadRequest(path, public, downloadOptions)
+ downloadOptions.httpRequestOverrides.forEach { it() }
}.body()
}
-
override suspend fun downloadAuthenticated(
path: String,
channel: ByteWriteChannel,
- transform: ImageTransformation.() -> Unit
+ options: DownloadOptionBuilder.() -> Unit
) {
- channelDownloadRequest(path, channel, false, transform)
+ channelDownloadRequest(path, channel, false, options)
}
override suspend fun downloadPublic(
path: String,
channel: ByteWriteChannel,
- transform: ImageTransformation.() -> Unit
+ options: DownloadOptionBuilder.() -> Unit
) {
- channelDownloadRequest(path, channel, true, transform)
+ channelDownloadRequest(path, channel, true, options)
}
internal suspend fun channelDownloadRequest(
path: String,
channel: ByteWriteChannel,
public: Boolean,
- transform: ImageTransformation.() -> Unit,
- extra: HttpRequestBuilder.() -> Unit = {}
+ options: DownloadOptionBuilder.() -> Unit,
) {
+ val downloadOptions = DownloadOptionBuilder().apply(options)
storage.api.prepareRequest {
- prepareDownloadRequest(path, public, transform)
- extra()
+ prepareDownloadRequest(path, public, downloadOptions)
+ downloadOptions.httpRequestOverrides.forEach { it() }
}.execute {
it.bodyAsChannel().copyTo(channel)
}
- channel.close()
+ channel.flushAndClose()
}
internal fun HttpRequestBuilder.prepareDownloadRequest(
path: String,
public: Boolean,
- transform: ImageTransformation.() -> Unit
+ options: DownloadOptionBuilder
) {
- val transformation = ImageTransformation().apply(transform).queryString()
+ val transformation = ImageTransformation().apply(options.transform).queryString()
val url = when (public) {
true -> if (transformation.isBlank()) publicUrl(path) else publicRenderUrl(
path,
- transform
+ options.transform
)
-
false -> if (transformation.isBlank()) authenticatedUrl(path) else authenticatedRenderUrl(
path,
- transform
+ options.transform
)
}
method = HttpMethod.Get
@@ -203,31 +213,29 @@ internal class BucketApiImpl(override val bucketId: String, val storage: Storage
override suspend fun list(
prefix: String,
filter: BucketListFilter.() -> Unit
- ): List {
+ ): List {
return storage.api.postJson("object/list/$bucketId", buildJsonObject {
put("prefix", prefix)
putJsonObject(BucketListFilter().apply(filter).build())
}).safeBody()
}
+ private fun defaultUploadUrl(path: String) = "object/$bucketId/$path"
+
+ private fun uploadToSignedUrlUrl(path: String, token: String) = "object/upload/sign/$bucketId/$path?token=$token"
+
internal suspend fun uploadOrUpdate(
method: HttpMethod,
- bucket: String,
- path: String,
+ url: String,
data: UploadData,
- upsert: Boolean,
- extra: HttpRequestBuilder.() -> Unit = {}
+ options: UploadOptionBuilder.() -> Unit,
): FileUploadResponse {
- val response = storage.api.request("object/$bucket/$path") {
+ val path = url.substringAfterLast('/').substringBeforeLast("?")
+ val optionBuilder = UploadOptionBuilder(storage.serializer).apply(options)
+ val response = storage.api.request(url) {
this.method = method
- setBody(object : OutgoingContent.ReadChannelContent() {
- override val contentType: ContentType = ContentType.defaultForFilePath(path)
- override val contentLength: Long = data.size
- override fun readFrom(): ByteReadChannel = data.stream
- })
- header(HttpHeaders.ContentType, ContentType.defaultForFilePath(path))
- header(UPSERT_HEADER, upsert.toString())
- extra()
+ defaultUploadRequest(path, data, optionBuilder)
+ optionBuilder.httpRequestOverrides.forEach { it() }
}.body()
val key = response["Key"]?.jsonPrimitive?.content
?: error("Expected a key in a upload response")
@@ -236,28 +244,18 @@ internal class BucketApiImpl(override val bucketId: String, val storage: Storage
return FileUploadResponse(id, path, key)
}
- internal suspend fun uploadToSignedUrl(
+ private fun HttpRequestBuilder.defaultUploadRequest(
path: String,
- token: String,
data: UploadData,
- upsert: Boolean,
- extra: HttpRequestBuilder.() -> Unit = {}
- ): FileUploadResponse {
- val response = storage.api.put("object/upload/sign/$bucketId/$path") {
- parameter("token", token)
- setBody(object : OutgoingContent.ReadChannelContent() {
- override val contentType: ContentType = ContentType.defaultForFilePath(path)
- override val contentLength: Long = data.size
- override fun readFrom(): ByteReadChannel = data.stream
- })
- header(HttpHeaders.ContentType, ContentType.defaultForFilePath(path))
- header("x-upsert", upsert.toString())
- extra()
- }.body()
- val key = response["Key"]?.jsonPrimitive?.content
- ?: error("Expected a key in a upload response")
- val id = response["Id"]?.jsonPrimitive?.content ?: error("Expected an id in a upload response")
- return FileUploadResponse(id, path, key)
+ optionBuilder: UploadOptionBuilder,
+ ) {
+ setBody(object : OutgoingContent.ReadChannelContent() {
+ override val contentType: ContentType = optionBuilder.contentType ?: ContentType.defaultForFilePath(path)
+ override val contentLength: Long = data.size
+ override fun readFrom(): ByteReadChannel = data.stream
+ })
+ header(HttpHeaders.ContentType, optionBuilder.contentType ?: ContentType.defaultForFilePath(path))
+ header(UPSERT_HEADER, optionBuilder.upsert.toString())
}
override suspend fun changePublicStatusTo(public: Boolean) = storage.updateBucket(bucketId) {
diff --git a/Storage/src/commonMain/kotlin/io/github/jan/supabase/storage/DownloadOptionBuilder.kt b/Storage/src/commonMain/kotlin/io/github/jan/supabase/storage/DownloadOptionBuilder.kt
new file mode 100644
index 000000000..9110f7d7b
--- /dev/null
+++ b/Storage/src/commonMain/kotlin/io/github/jan/supabase/storage/DownloadOptionBuilder.kt
@@ -0,0 +1,29 @@
+package io.github.jan.supabase.storage
+
+import io.github.jan.supabase.network.HttpRequestOverride
+
+/**
+ * Builder for downloading files with additional options
+ */
+class DownloadOptionBuilder(
+ internal var transform: ImageTransformation.() -> Unit = {},
+ internal val httpRequestOverrides: MutableList = mutableListOf()
+) {
+
+ /**
+ * Transforms the image before downloading
+ * @param transform The transformation to apply
+ */
+ fun transform(transform: ImageTransformation.() -> Unit) {
+ this.transform = transform
+ }
+
+ /**
+ * Overrides the HTTP request
+ * @param override The override to apply
+ */
+ fun httpOverride(override: HttpRequestOverride) {
+ httpRequestOverrides.add(override)
+ }
+
+}
\ No newline at end of file
diff --git a/Storage/src/commonMain/kotlin/io/github/jan/supabase/storage/BucketItem.kt b/Storage/src/commonMain/kotlin/io/github/jan/supabase/storage/FileObject.kt
similarity index 97%
rename from Storage/src/commonMain/kotlin/io/github/jan/supabase/storage/BucketItem.kt
rename to Storage/src/commonMain/kotlin/io/github/jan/supabase/storage/FileObject.kt
index 595eb8d96..8caf02a8a 100644
--- a/Storage/src/commonMain/kotlin/io/github/jan/supabase/storage/BucketItem.kt
+++ b/Storage/src/commonMain/kotlin/io/github/jan/supabase/storage/FileObject.kt
@@ -15,7 +15,7 @@ import kotlinx.serialization.json.JsonObject
* @param metadata The metadata of the item
*/
@Serializable
-data class BucketItem(
+data class FileObject(
val name: String,
val id: String?,
@SerialName("updated_at")
diff --git a/Storage/src/commonMain/kotlin/io/github/jan/supabase/storage/FlowExt.kt b/Storage/src/commonMain/kotlin/io/github/jan/supabase/storage/FlowExt.kt
deleted file mode 100644
index d473c0972..000000000
--- a/Storage/src/commonMain/kotlin/io/github/jan/supabase/storage/FlowExt.kt
+++ /dev/null
@@ -1,225 +0,0 @@
-package io.github.jan.supabase.storage
-
-import io.github.jan.supabase.exceptions.HttpRequestException
-import io.github.jan.supabase.exceptions.RestException
-import io.ktor.client.call.body
-import io.ktor.client.plugins.HttpRequestTimeoutException
-import io.ktor.client.plugins.onDownload
-import io.ktor.client.plugins.onUpload
-import io.ktor.http.HttpMethod
-import io.ktor.utils.io.ByteReadChannel
-import io.ktor.utils.io.ByteWriteChannel
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.callbackFlow
-
-/**
- * Uploads a file in [bucketId] under [path]
- * @param path The path to upload the file to
- * @param upsert Whether to overwrite an existing file
- * @param data The data to upload
- * @return A flow that emits the upload progress and at last the key to the uploaded file
- * @throws RestException or one of its subclasses if receiving an error response
- * @throws HttpRequestTimeoutException if the request timed out
- * @throws HttpRequestException on network related issues
- */
-fun BucketApi.updateAsFlow(path: String, data: UploadData, upsert: Boolean = false): Flow = callbackFlow {
- this@updateAsFlow as BucketApiImpl
- val key = uploadOrUpdate(HttpMethod.Put, bucketId, path, data, upsert) {
- onUpload { bytesSentTotal, contentLength ->
- trySend(UploadStatus.Progress(bytesSentTotal, contentLength))
- }
- }
- trySend(UploadStatus.Success(key))
- close()
-}
-
-/**
- * Uploads a file in [bucketId] under [path]
- * @param path The path to upload the file to
- * @param upsert Whether to overwrite an existing file
- * @param data The data to upload
- * @return A flow that emits the upload progress and at last the key to the uploaded file
- * @throws RestException or one of its subclasses if receiving an error response
- * @throws HttpRequestTimeoutException if the request timed out
- * @throws HttpRequestException on network related issues
- */
-fun BucketApi.uploadAsFlow(path: String, data: ByteArray, upsert: Boolean = false): Flow = uploadAsFlow(path, UploadData(
- ByteReadChannel(data), data.size.toLong()), upsert)
-
-/**
- * Uploads a file in [bucketId] under [path] using a presigned url
- * @param path The path to upload the file to
- * @param token The presigned url token
- * @param data The data to upload
- * @param upsert Whether to overwrite an existing file
- * @return A flow that emits the upload progress and at last the key to the uploaded file
- * @throws RestException or one of its subclasses if receiving an error response
- * @throws HttpRequestTimeoutException if the request timed out
- * @throws HttpRequestException on network related issues
- */
-fun BucketApi.uploadToSignedUrlAsFlow(
- path: String,
- token: String,
- data: UploadData,
- upsert: Boolean = false
-): Flow {
- return callbackFlow {
- this@uploadToSignedUrlAsFlow as BucketApiImpl
- val key = uploadToSignedUrl(path, token, data, upsert) {
- onUpload { bytesSentTotal, contentLength ->
- trySend(UploadStatus.Progress(bytesSentTotal, contentLength))
- }
- }
- trySend(UploadStatus.Success(key))
- close()
- }
-}
-
-/**
- * Uploads a file in [bucketId] under [path] using a presigned url
- * @param path The path to upload the file to
- * @param token The presigned url token
- * @param data The data to upload
- * @param upsert Whether to overwrite an existing file
- * @return A flow that emits the upload progress and at last the key to the uploaded file
- */
-fun BucketApi.uploadToSignedUrlAsFlow(path: String, token: String, data: ByteArray, upsert: Boolean = false): Flow = uploadToSignedUrlAsFlow(path, token, UploadData(ByteReadChannel(data), data.size.toLong()), upsert)
-
-/**
- * Updates a file in [bucketId] under [path]
- * @param path The path to update the file to
- * @param data The new data
- * @param upsert Whether to overwrite an existing file
- * @return A flow that emits the upload progress and at last the key to the uploaded file
- * @throws RestException or one of its subclasses if receiving an error response
- * @throws HttpRequestTimeoutException if the request timed out
- * @throws HttpRequestException on network related issues
- */
-fun BucketApi.uploadAsFlow(path: String, data: UploadData, upsert: Boolean = false): Flow {
- return callbackFlow {
- this@uploadAsFlow as BucketApiImpl
- val key = uploadOrUpdate(HttpMethod.Post, bucketId, path, data, upsert) {
- onUpload { bytesSentTotal, contentLength ->
- trySend(UploadStatus.Progress(bytesSentTotal, contentLength))
- }
- }
- trySend(UploadStatus.Success(key))
- close()
- }
-}
-
-/**
- * Updates a file in [bucketId] under [path]
- * @param path The path to update the file to
- * @param data The new data
- * @param upsert Whether to overwrite an existing file
- * @return A flow that emits the upload progress and at last the key to the uploaded file
- * @throws RestException or one of its subclasses if receiving an error response
- * @throws HttpRequestTimeoutException if the request timed out
- * @throws HttpRequestException on network related issues
- */
-fun BucketApi.updateAsFlow(path: String, data: ByteArray, upsert: Boolean = false): Flow = updateAsFlow(path, UploadData(ByteReadChannel(data), data.size.toLong()), upsert)
-
-/**
- * Downloads a file from [bucketId] under [path]
- * @param path The path to download
- * @param transform The transformation to apply to the image
- * @return A flow that emits the download progress and at last the data as a byte array
- * @throws RestException or one of its subclasses if receiving an error response
- * @throws HttpRequestTimeoutException if the request timed out
- * @throws HttpRequestException on network related issues
- */
-fun BucketApi.downloadAuthenticatedAsFlow(
- path: String,
- transform: ImageTransformation.() -> Unit = {}
-): Flow {
- return callbackFlow {
- this@downloadAuthenticatedAsFlow as BucketApiImpl
- val data = storage.api.rawRequest {
- prepareDownloadRequest(path, false, transform)
- onDownload { bytesSentTotal, contentLength ->
- trySend(DownloadStatus.Progress(bytesSentTotal, contentLength))
- }
- }.body()
- trySend(DownloadStatus.Success)
- trySend(DownloadStatus.ByteData(data))
- close()
- }
-}
-
-/**
- * Downloads a file from [bucketId] under [path]
- * @param path The path to download
- * @param channel The channel to write the data to
- * @param transform The transformation to apply to the image
- * @return A flow that emits the download progress and at last the data as a byte array
- * @throws RestException or one of its subclasses if receiving an error response
- * @throws HttpRequestTimeoutException if the request timed out
- * @throws HttpRequestException on network related issues
- */
-fun BucketApi.downloadAuthenticatedAsFlow(
- path: String,
- channel: ByteWriteChannel,
- transform: ImageTransformation.() -> Unit = {}
-): Flow {
- this as BucketApiImpl
- return flowChannelDownloadRequest(path, channel, false, transform)
-}
-
-/**
- * Downloads a file from [bucketId] under [path] using the public url
- * @param path The path to download
- * @param transform The transformation to apply to the image
- * @return A flow that emits the download progress and at last the data as a byte array
- * @throws RestException or one of its subclasses if receiving an error response
- * @throws HttpRequestTimeoutException if the request timed out
- * @throws HttpRequestException on network related issues
- */
-fun BucketApi.downloadPublicAsFlow(path: String, transform: ImageTransformation.() -> Unit = {}): Flow {
- return callbackFlow {
- this@downloadPublicAsFlow as BucketApiImpl
- val data = storage.api.rawRequest {
- prepareDownloadRequest(path, true, transform)
- onDownload { bytesSentTotal, contentLength ->
- trySend(DownloadStatus.Progress(bytesSentTotal, contentLength))
- }
- }.body()
- trySend(DownloadStatus.Success)
- trySend(DownloadStatus.ByteData(data))
- close()
- }
-}
-
-/**
- * Downloads a file from [bucketId] under [path] using the public url
- * @param path The path to download
- * @param channel The channel to write the data to
- * @param transform The transformation to apply to the image
- * @return A flow that emits the download progress and at last the data as a byte array
- * @throws RestException or one of its subclasses if receiving an error response
- * @throws HttpRequestTimeoutException if the request timed out
- * @throws HttpRequestException on network related issues
- */
-fun BucketApi.downloadPublicAsFlow(
- path: String,
- channel: ByteWriteChannel,
- transform: ImageTransformation.() -> Unit = {}
-): Flow {
- this as BucketApiImpl
- return flowChannelDownloadRequest(path, channel, true, transform)
-}
-
-private fun BucketApiImpl.flowChannelDownloadRequest(
- path: String,
- channel: ByteWriteChannel,
- public: Boolean,
- transform: ImageTransformation.() -> Unit
-): Flow = callbackFlow {
- channelDownloadRequest(path, channel, public, transform) {
- onDownload { bytesSentTotal, contentLength ->
- trySend(DownloadStatus.Progress(bytesSentTotal, contentLength))
- }
- }
- trySend(DownloadStatus.Success)
- close()
-}
\ No newline at end of file
diff --git a/Storage/src/commonMain/kotlin/io/github/jan/supabase/storage/FlowExtension.kt b/Storage/src/commonMain/kotlin/io/github/jan/supabase/storage/FlowExtension.kt
new file mode 100644
index 000000000..146b0bbb1
--- /dev/null
+++ b/Storage/src/commonMain/kotlin/io/github/jan/supabase/storage/FlowExtension.kt
@@ -0,0 +1,229 @@
+package io.github.jan.supabase.storage
+
+import io.github.jan.supabase.exceptions.HttpRequestException
+import io.github.jan.supabase.exceptions.RestException
+import io.github.jan.supabase.network.HttpRequestOverride
+import io.ktor.client.plugins.HttpRequestTimeoutException
+import io.ktor.client.plugins.onDownload
+import io.ktor.client.plugins.onUpload
+import io.ktor.utils.io.ByteReadChannel
+import io.ktor.utils.io.ByteWriteChannel
+import kotlinx.coroutines.channels.ProducerScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.callbackFlow
+
+private fun downloadOverride(flowProducer: ProducerScope): HttpRequestOverride = {
+ onDownload { bytesSentTotal, contentLength ->
+ flowProducer.trySend(DownloadStatus.Progress(bytesSentTotal, contentLength ?: 0))
+ }
+}
+
+private fun uploadOverride(flowProducer: ProducerScope): HttpRequestOverride = {
+ onUpload { bytesSentTotal, contentLength ->
+ flowProducer.trySend(UploadStatus.Progress(bytesSentTotal, contentLength ?: 0))
+ }
+}
+
+/**
+ * Uploads a file in [BucketApi.bucketId] under [path]
+ * @param path The path to upload the file to
+ * @param options Additional options for the upload
+ * @param data The data to upload
+ * @return A flow that emits the upload progress and at last the key to the uploaded file
+ * @throws RestException or one of its subclasses if receiving an error response
+ * @throws HttpRequestTimeoutException if the request timed out
+ * @throws HttpRequestException on network related issues
+ */
+fun BucketApi.updateAsFlow(
+ path: String,
+ data: UploadData,
+ options: UploadOptionBuilder.() -> Unit = {}
+): Flow = uploadAsFlowRequest {
+ update(path, data) {
+ options()
+ httpOverride(it)
+ }
+}
+
+/**
+ * Uploads a file in [BucketApi.bucketId] under [path]
+ * @param path The path to upload the file to
+ * @param options Additional options for the upload
+ * @param data The data to upload
+ * @return A flow that emits the upload progress and at last the key to the uploaded file
+ * @throws RestException or one of its subclasses if receiving an error response
+ * @throws HttpRequestTimeoutException if the request timed out
+ * @throws HttpRequestException on network related issues
+ */
+fun BucketApi.uploadAsFlow(path: String, data: ByteArray, options: UploadOptionBuilder.() -> Unit = {}): Flow = uploadAsFlow(path, UploadData(
+ ByteReadChannel(data), data.size.toLong()), options)
+
+/**
+ * Uploads a file in [BucketApi.bucketId] under [path] using a presigned url
+ * @param path The path to upload the file to
+ * @param token The presigned url token
+ * @param data The data to upload
+ * @param options Additional options for the upload
+ * @return A flow that emits the upload progress and at last the key to the uploaded file
+ * @throws RestException or one of its subclasses if receiving an error response
+ * @throws HttpRequestTimeoutException if the request timed out
+ * @throws HttpRequestException on network related issues
+ */
+fun BucketApi.uploadToSignedUrlAsFlow(
+ path: String,
+ token: String,
+ data: UploadData,
+ options: UploadOptionBuilder.() -> Unit = {}
+): Flow = uploadAsFlowRequest {
+ uploadToSignedUrl(path, token, data) {
+ options()
+ httpOverride(it)
+ }
+}
+
+/**
+ * Uploads a file in [BucketApi.bucketId] under [path] using a presigned url
+ * @param path The path to upload the file to
+ * @param token The presigned url token
+ * @param data The data to upload
+ * @param options Additional options for the upload
+ * @return A flow that emits the upload progress and at last the key to the uploaded file
+ */
+fun BucketApi.uploadToSignedUrlAsFlow(path: String, token: String, data: ByteArray, options: UploadOptionBuilder.() -> Unit = {}): Flow = uploadToSignedUrlAsFlow(path, token, UploadData(ByteReadChannel(data), data.size.toLong()), options)
+
+/**
+ * Updates a file in [BucketApi.bucketId] under [path]
+ * @param path The path to update the file to
+ * @param data The new data
+ * @param options Additional options for the upload
+ * @return A flow that emits the upload progress and at last the key to the uploaded file
+ * @throws RestException or one of its subclasses if receiving an error response
+ * @throws HttpRequestTimeoutException if the request timed out
+ * @throws HttpRequestException on network related issues
+ */
+fun BucketApi.uploadAsFlow(
+ path: String,
+ data: UploadData,
+ options: UploadOptionBuilder.() -> Unit = {}
+): Flow = uploadAsFlowRequest {
+ upload(path, data) {
+ options()
+ httpOverride(it)
+ }
+}
+
+/**
+ * Updates a file in [BucketApi.bucketId] under [path]
+ * @param path The path to update the file to
+ * @param data The new data
+ * @param options Additional options for the upload
+ * @return A flow that emits the upload progress and at last the key to the uploaded file
+ * @throws RestException or one of its subclasses if receiving an error response
+ * @throws HttpRequestTimeoutException if the request timed out
+ * @throws HttpRequestException on network related issues
+ */
+fun BucketApi.updateAsFlow(path: String, data: ByteArray, options: UploadOptionBuilder.() -> Unit = {}): Flow = updateAsFlow(path, UploadData(ByteReadChannel(data), data.size.toLong()), options)
+
+private fun BucketApi.uploadAsFlowRequest(
+ producer: suspend (HttpRequestOverride) -> FileUploadResponse
+) = callbackFlow {
+ val key = producer(uploadOverride(this@callbackFlow))
+ trySend(UploadStatus.Success(key))
+ close()
+}
+
+/**
+ * Downloads a file from [BucketApi.bucketId] under [path]
+ * @param path The path to download
+ * @param options Additional options for the download
+ * @return A flow that emits the download progress and at last the data as a byte array
+ * @throws RestException or one of its subclasses if receiving an error response
+ * @throws HttpRequestTimeoutException if the request timed out
+ * @throws HttpRequestException on network related issues
+ */
+fun BucketApi.downloadAuthenticatedAsFlow(
+ path: String,
+ options: DownloadOptionBuilder.() -> Unit = {}
+): Flow = downloadAsFlowRequest {
+ downloadAuthenticated(path) {
+ options()
+ httpOverride(it)
+ }
+}
+
+/**
+ * Downloads a file from [BucketApi.bucketId] under [path] using the public url
+ * @param path The path to download
+ * @param options Additional options for the download
+ * @return A flow that emits the download progress and at last the data as a byte array
+ * @throws RestException or one of its subclasses if receiving an error response
+ * @throws HttpRequestTimeoutException if the request timed out
+ * @throws HttpRequestException on network related issues
+ */
+fun BucketApi.downloadPublicAsFlow(
+ path: String,
+ options: DownloadOptionBuilder.() -> Unit = {}
+): Flow =
+ downloadAsFlowRequest {
+ downloadPublic(path) {
+ options()
+ httpOverride(it)
+ }
+ }
+
+
+/**
+ * Downloads a file from [BucketApi.bucketId] under [path]
+ * @param path The path to download
+ * @param channel The channel to write the data to
+ * @param options Additional options for the download
+ * @return A flow that emits the download progress and at last the data as a byte array
+ * @throws RestException or one of its subclasses if receiving an error response
+ * @throws HttpRequestTimeoutException if the request timed out
+ * @throws HttpRequestException on network related issues
+ */
+fun BucketApi.downloadAuthenticatedAsFlow(
+ path: String,
+ channel: ByteWriteChannel,
+ options: DownloadOptionBuilder.() -> Unit = {}
+): Flow = downloadAsFlowRequest {
+ downloadAuthenticated(path, channel) {
+ options()
+ httpOverride(it)
+ }
+ null
+}
+
+/**
+ * Downloads a file from [BucketApi.bucketId] under [path] using the public url
+ * @param path The path to download
+ * @param channel The channel to write the data to
+ * @param options Additional options for the download
+ * @return A flow that emits the download progress and at last the data as a byte array
+ * @throws RestException or one of its subclasses if receiving an error response
+ * @throws HttpRequestTimeoutException if the request timed out
+ * @throws HttpRequestException on network related issues
+ */
+fun BucketApi.downloadPublicAsFlow(
+ path: String,
+ channel: ByteWriteChannel,
+ options: DownloadOptionBuilder.() -> Unit = {}
+): Flow = downloadAsFlowRequest {
+ downloadPublic(path, channel) {
+ options()
+ httpOverride(it)
+ }
+ null
+}
+
+private fun BucketApi.downloadAsFlowRequest(
+ producer: suspend (HttpRequestOverride) -> ByteArray?
+) = callbackFlow {
+ //If null, the data gets streamed to a channel, so we don't need to emit it later
+ val data = producer(downloadOverride(this@callbackFlow))
+ trySend(DownloadStatus.Success)
+ if (data != null) {
+ trySend(DownloadStatus.ByteData(data))
+ }
+ close()
+}
diff --git a/Storage/src/commonMain/kotlin/io/github/jan/supabase/storage/Storage.kt b/Storage/src/commonMain/kotlin/io/github/jan/supabase/storage/Storage.kt
index 64eea8f3c..53bb5a247 100644
--- a/Storage/src/commonMain/kotlin/io/github/jan/supabase/storage/Storage.kt
+++ b/Storage/src/commonMain/kotlin/io/github/jan/supabase/storage/Storage.kt
@@ -1,7 +1,9 @@
package io.github.jan.supabase.storage
import io.github.jan.supabase.SupabaseClient
+import io.github.jan.supabase.SupabaseSerializer
import io.github.jan.supabase.annotations.SupabaseInternal
+import io.github.jan.supabase.auth.authenticatedSupabaseApi
import io.github.jan.supabase.bodyOrNull
import io.github.jan.supabase.collections.AtomicMutableMap
import io.github.jan.supabase.exceptions.BadRequestRestException
@@ -10,9 +12,10 @@ import io.github.jan.supabase.exceptions.NotFoundRestException
import io.github.jan.supabase.exceptions.RestException
import io.github.jan.supabase.exceptions.UnauthorizedRestException
import io.github.jan.supabase.exceptions.UnknownRestException
-import io.github.jan.supabase.gotrue.authenticatedSupabaseApi
import io.github.jan.supabase.logging.SupabaseLogger
import io.github.jan.supabase.logging.w
+import io.github.jan.supabase.plugins.CustomSerializationConfig
+import io.github.jan.supabase.plugins.CustomSerializationPlugin
import io.github.jan.supabase.plugins.MainConfig
import io.github.jan.supabase.plugins.MainPlugin
import io.github.jan.supabase.plugins.SupabasePluginProvider
@@ -46,7 +49,7 @@ import kotlin.time.Duration.Companion.seconds
* val bytes = bucket.downloadAuthenticated("icon.png")
* ```
*/
-sealed interface Storage : MainPlugin {
+sealed interface Storage : MainPlugin, CustomSerializationPlugin {
/**
* Creates a new bucket in the storage
@@ -116,8 +119,9 @@ sealed interface Storage : MainPlugin {
*/
data class Config(
var transferTimeout: Duration = 120.seconds,
- @PublishedApi internal var resumable: Resumable = Resumable()
- ) : MainConfig() {
+ @PublishedApi internal var resumable: Resumable = Resumable(),
+ override var serializer: SupabaseSerializer? = null
+ ) : MainConfig(), CustomSerializationConfig {
/**
* @param cache the cache for caching resumable upload urls
@@ -185,6 +189,8 @@ internal class StorageImpl(override val supabaseClient: SupabaseClient, override
override val apiVersion: Int
get() = Storage.API_VERSION
+ override val serializer: SupabaseSerializer = config.serializer ?: supabaseClient.defaultSerializer
+
@OptIn(SupabaseInternal::class)
internal val api = supabaseClient.authenticatedSupabaseApi(this) {
timeout {
diff --git a/Storage/src/commonMain/kotlin/io/github/jan/supabase/storage/UploadOptionBuilder.kt b/Storage/src/commonMain/kotlin/io/github/jan/supabase/storage/UploadOptionBuilder.kt
new file mode 100644
index 000000000..61bfa326e
--- /dev/null
+++ b/Storage/src/commonMain/kotlin/io/github/jan/supabase/storage/UploadOptionBuilder.kt
@@ -0,0 +1,27 @@
+package io.github.jan.supabase.storage
+
+import io.github.jan.supabase.SupabaseSerializer
+import io.github.jan.supabase.network.HttpRequestOverride
+import io.ktor.http.ContentType
+
+/**
+ * Builder for uploading files with additional options
+ * @param serializer The serializer to use for encoding the metadata
+ * @param upsert Whether to update the file if it already exists
+ * @param contentType The content type of the file. If null, the content type will be inferred from the file extension
+ */
+class UploadOptionBuilder(
+ @PublishedApi internal val serializer: SupabaseSerializer,
+ var upsert: Boolean = false,
+ var contentType: ContentType? = null,
+ internal val httpRequestOverrides: MutableList = mutableListOf()
+) {
+
+ /**
+ * Overrides the HTTP request
+ */
+ fun httpOverride(override: HttpRequestOverride) {
+ httpRequestOverrides.add(override)
+ }
+
+}
diff --git a/Storage/src/commonMain/kotlin/io/github/jan/supabase/storage/resumable/ResumableClient.kt b/Storage/src/commonMain/kotlin/io/github/jan/supabase/storage/resumable/ResumableClient.kt
index bd866dede..3e4a616bf 100644
--- a/Storage/src/commonMain/kotlin/io/github/jan/supabase/storage/resumable/ResumableClient.kt
+++ b/Storage/src/commonMain/kotlin/io/github/jan/supabase/storage/resumable/ResumableClient.kt
@@ -3,7 +3,7 @@
package io.github.jan.supabase.storage.resumable
import io.github.jan.supabase.annotations.SupabaseInternal
-import io.github.jan.supabase.gotrue.Auth
+import io.github.jan.supabase.auth.Auth
import io.github.jan.supabase.logging.d
import io.github.jan.supabase.storage.BucketApi
import io.github.jan.supabase.storage.Storage
@@ -21,6 +21,7 @@ import io.ktor.http.defaultForFilePath
import io.ktor.http.isSuccess
import io.ktor.utils.io.ByteReadChannel
import io.ktor.utils.io.core.toByteArray
+import io.ktor.utils.io.discard
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
diff --git a/Storage/src/commonMain/kotlin/io/github/jan/supabase/storage/resumable/ResumableUpload.kt b/Storage/src/commonMain/kotlin/io/github/jan/supabase/storage/resumable/ResumableUpload.kt
index b8f557b5e..fb761c98e 100644
--- a/Storage/src/commonMain/kotlin/io/github/jan/supabase/storage/resumable/ResumableUpload.kt
+++ b/Storage/src/commonMain/kotlin/io/github/jan/supabase/storage/resumable/ResumableUpload.kt
@@ -1,7 +1,7 @@
package io.github.jan.supabase.storage.resumable
import io.github.jan.supabase.annotations.SupabaseInternal
-import io.github.jan.supabase.gotrue.Auth
+import io.github.jan.supabase.auth.Auth
import io.github.jan.supabase.logging.d
import io.github.jan.supabase.logging.e
import io.github.jan.supabase.logging.w
@@ -21,6 +21,8 @@ import io.ktor.client.statement.bodyAsText
import io.ktor.http.HttpStatusCode
import io.ktor.utils.io.ByteReadChannel
import io.ktor.utils.io.cancel
+import io.ktor.utils.io.readAvailable
+import io.ktor.utils.io.writeFully
import kotlinx.atomicfu.atomic
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -69,6 +71,7 @@ sealed interface ResumableUpload {
}
+@Suppress("LongParameterList") //TODO: refactor
internal class ResumableUploadImpl(
override val fingerprint: Fingerprint,
private val path: String,
@@ -149,7 +152,8 @@ internal class ResumableUploadImpl(
private suspend fun uploadChunk(): Int {
val limit = min(chunkSize, size.toInt() - offset)
val buffer = ByteArray(limit.toInt())
- dataStream.readFully(buffer, 0, limit.toInt())
+ dataStream.readAvailable(buffer, 0, limit.toInt())
+ //dataStream.readFully(buffer, 0, limit.toInt())
val uploadResponse = httpClient.patch(locationUrl) {
header("Tus-Resumable", TUS_VERSION)
header("Content-Type", "application/offset+octet-stream")
diff --git a/Storage/src/commonTest/kotlin/BucketApiFlowTest.kt b/Storage/src/commonTest/kotlin/BucketApiFlowTest.kt
index c0eb57575..939a73065 100644
--- a/Storage/src/commonTest/kotlin/BucketApiFlowTest.kt
+++ b/Storage/src/commonTest/kotlin/BucketApiFlowTest.kt
@@ -73,7 +73,9 @@ class BucketApiFlowTest {
@Test
fun testUpsertAsFlowMethodWithByteArray() {
testUploadAsFlow(upsert = true) { client, expectedPath, data ->
- val flow = client.storage[bucketId].uploadAsFlow(expectedPath, data, upsert = true)
+ val flow = client.storage[bucketId].uploadAsFlow(expectedPath, data) {
+ upsert = true
+ }
testUploadFlowWithByteArray(flow, expectedPath, data)
}
}
@@ -95,7 +97,9 @@ class BucketApiFlowTest {
val channel = ByteReadChannel(bytes)
val expectedSize = bytes.size.toLong()
val data = UploadData(channel, expectedSize)
- val flow = client.storage[bucketId].uploadAsFlow(expectedPath, data, upsert = true)
+ val flow = client.storage[bucketId].uploadAsFlow(expectedPath, data) {
+ upsert = true
+ }
testUploadFlowWithByteArray(flow, expectedPath, bytes)
}
}
@@ -131,7 +135,9 @@ class BucketApiFlowTest {
@Test
fun testUpdateUpsertAsFlowMethodWithByteArray() {
testUpdateAsFlow(upsert = true) { client, expectedPath, data ->
- val flow = client.storage[bucketId].updateAsFlow(expectedPath, data, upsert = true)
+ val flow = client.storage[bucketId].updateAsFlow(expectedPath, data) {
+ upsert = true
+ }
testUploadFlowWithByteArray(flow, expectedPath, data)
}
}
@@ -153,7 +159,9 @@ class BucketApiFlowTest {
val channel = ByteReadChannel(bytes)
val expectedSize = bytes.size.toLong()
val data = UploadData(channel, expectedSize)
- val flow = client.storage[bucketId].updateAsFlow(expectedPath, data, upsert = true)
+ val flow = client.storage[bucketId].updateAsFlow(expectedPath, data) {
+ upsert = true
+ }
testUploadFlowWithByteArray(flow, expectedPath, bytes)
}
}
@@ -206,7 +214,9 @@ class BucketApiFlowTest {
val channel = ByteReadChannel(bytes)
val expectedSize = bytes.size.toLong()
val data = UploadData(channel, expectedSize)
- val flow = client.storage[bucketId].uploadToSignedUrlAsFlow(expectedPath, expectedToken, data, true)
+ val flow = client.storage[bucketId].uploadToSignedUrlAsFlow(expectedPath, expectedToken, data) {
+ upsert = true
+ }
testUploadFlowWithByteArray(flow, expectedPath, bytes)
}
}
@@ -214,7 +224,9 @@ class BucketApiFlowTest {
@Test
fun testUpsertToSignedUrlAsFlowMethodByteArray() {
testUploadToSignedUrl(upsert = true) { client, expectedPath, bytes ->
- val flow = client.storage[bucketId].uploadToSignedUrlAsFlow(expectedPath, expectedToken, bytes, true)
+ val flow = client.storage[bucketId].uploadToSignedUrlAsFlow(expectedPath, expectedToken, bytes) {
+ upsert = true
+ }
testUploadFlowWithByteArray(flow, expectedPath, bytes)
}
}
diff --git a/Storage/src/commonTest/kotlin/BucketApiTest.kt b/Storage/src/commonTest/kotlin/BucketApiTest.kt
index b5e56849e..ece065241 100644
--- a/Storage/src/commonTest/kotlin/BucketApiTest.kt
+++ b/Storage/src/commonTest/kotlin/BucketApiTest.kt
@@ -21,11 +21,14 @@ import io.ktor.http.HttpMethod
import io.ktor.http.headersOf
import kotlinx.coroutines.test.runTest
import kotlinx.datetime.Clock
+import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.int
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import kotlinx.serialization.json.long
+import kotlinx.serialization.json.put
+import kotlin.io.encoding.ExperimentalEncodingApi
import kotlin.test.Test
import kotlin.test.assertContentEquals
import kotlin.test.assertEquals
@@ -74,7 +77,9 @@ class BucketApiTest {
)
},
request = { client, expectedPath, data ->
- client.storage[bucketId].upload(expectedPath, data, upsert = true)
+ client.storage[bucketId].upload(expectedPath, data) {
+ upsert = true
+ }
}
)
}
@@ -98,7 +103,9 @@ class BucketApiTest {
)
},
request = { client, expectedPath, data ->
- client.storage[bucketId].uploadToSignedUrl(path = expectedPath, token = expectedToken, data = data, upsert = false)
+ client.storage[bucketId].uploadToSignedUrl(path = expectedPath, token = expectedToken, data = data) {
+ upsert = false
+ }
}
)
}
@@ -122,7 +129,9 @@ class BucketApiTest {
)
},
request = { client, expectedPath, data ->
- client.storage[bucketId].uploadToSignedUrl(path = expectedPath, token = expectedToken, data = data, upsert = true)
+ client.storage[bucketId].uploadToSignedUrl(path = expectedPath, token = expectedToken, data = data) {
+ upsert = true
+ }
}
)
}
@@ -158,7 +167,9 @@ class BucketApiTest {
)
},
request = { client, expectedPath, data ->
- client.storage[bucketId].update(expectedPath, data, upsert = true)
+ client.storage[bucketId].update(expectedPath, data) {
+ upsert = true
+ }
}
)
}
@@ -458,11 +469,12 @@ class BucketApiTest {
quality = expectedQuality
resize = expectedResize
}
- val data = if(authenticated) client.storage[bucketId].downloadAuthenticated(expectedPath, transform) else client.storage[bucketId].downloadPublic(expectedPath, transform)
+ val data = if(authenticated) client.storage[bucketId].downloadAuthenticated(expectedPath) { transform(transform) } else client.storage[bucketId].downloadPublic(expectedPath) { transform(transform) }
assertContentEquals(expectedData, data, "Data should be [1, 2, 3]")
}
}
+ @OptIn(ExperimentalEncodingApi::class)
private fun testUploadMethod(
method: HttpMethod,
urlPath: String,
@@ -472,6 +484,9 @@ class BucketApiTest {
) {
runTest {
val expectedData = byteArrayOf(1, 2, 3)
+ val expectedMetadata = buildJsonObject {
+ put("key", "value")
+ }
val client = createMockedSupabaseClient(configuration = configureClient) {
val data = it.body.toByteArray()
assertMethodIs(method, it.method)
diff --git a/Storage/src/commonTest/kotlin/StorageTest.kt b/Storage/src/commonTest/kotlin/StorageTest.kt
index efa878bd3..ab972775f 100644
--- a/Storage/src/commonTest/kotlin/StorageTest.kt
+++ b/Storage/src/commonTest/kotlin/StorageTest.kt
@@ -1,7 +1,7 @@
import io.github.jan.supabase.SupabaseClientBuilder
-import io.github.jan.supabase.gotrue.Auth
-import io.github.jan.supabase.gotrue.auth
-import io.github.jan.supabase.gotrue.minimalSettings
+import io.github.jan.supabase.auth.Auth
+import io.github.jan.supabase.auth.auth
+import io.github.jan.supabase.auth.minimalSettings
import io.github.jan.supabase.storage.Storage
import io.github.jan.supabase.storage.resumable.MemoryResumableCache
import io.github.jan.supabase.storage.storage
diff --git a/Storage/src/settingsMain/kotlin/io/github/jan/supabase/storage/resumable/SettingsResumableCache.kt b/Storage/src/settingsMain/kotlin/io/github/jan/supabase/storage/resumable/SettingsResumableCache.kt
index 80036697a..9e558d836 100644
--- a/Storage/src/settingsMain/kotlin/io/github/jan/supabase/storage/resumable/SettingsResumableCache.kt
+++ b/Storage/src/settingsMain/kotlin/io/github/jan/supabase/storage/resumable/SettingsResumableCache.kt
@@ -12,29 +12,25 @@ import kotlinx.serialization.json.Json
* A [ResumableCache] implementation using [com.russhwolf.settings.Settings]. This implementation saves the urls on the disk. If you want a memory only cache, use [Memory].
* Unsupported on Linux.
*/
+@OptIn(ExperimentalSettingsApi::class)
class SettingsResumableCache(settings: Settings = Settings()) : ResumableCache {
- @OptIn(ExperimentalSettingsApi::class)
private val settings = settings.toSuspendSettings()
- @OptIn(ExperimentalSettingsApi::class)
override suspend fun set(fingerprint: Fingerprint, entry: ResumableCacheEntry) {
settings.putString(fingerprint.value, Json.encodeToString(entry))
}
- @OptIn(ExperimentalSettingsApi::class)
override suspend fun get(fingerprint: Fingerprint): ResumableCacheEntry? {
return settings.getStringOrNull(fingerprint.value)?.let {
Json.decodeFromString(it)
}
}
- @OptIn(ExperimentalSettingsApi::class)
override suspend fun remove(fingerprint: Fingerprint) {
settings.remove(fingerprint.value)
}
- @OptIn(ExperimentalSettingsApi::class)
override suspend fun clear() {
settings.keys().forEach {
if(it.split(Fingerprint.FINGERPRINT_SEPARATOR).size == Fingerprint.FINGERPRINT_PARTS) remove(Fingerprint(it) ?: error("Invalid fingerprint $it"))
diff --git a/Supabase/README.md b/Supabase/README.md
index c1db735a0..af076ae14 100644
--- a/Supabase/README.md
+++ b/Supabase/README.md
@@ -4,23 +4,27 @@ The main Supabase-kt library. It provides a plugin system to extend the client w
Supported targets:
-| Target | **JVM** | **Android** | **JS** | **iOS** | **tvOS** | **watchOS** | **macOS** | **Windows** | **Linux** |
-| ------ | ------- | ----------- | ------ | ------- | -------- | ----------- | --------- | ----------- | --------- |
-| | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
+| Target | **JVM** | **Android** | **JS** | **Wasm** | **Apple** | **Windows** | **Linux** |
+|--------|---------|-------------|--------|----------|-----------|-------------|-----------|
+| Status | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
In-depth Kotlin targets
-**iOS:** iosArm64, iosSimulatorArm64, iosX64
-
**JS**: Browser, NodeJS
-**tvOS**: tvosArm64, tvosX64, tvosSimulatorArm64
+**Wasm**: wasm-js
+
+**Apple:**
+
+- iOS: iosArm64, iosSimulatorArm64, iosX64
+
+- tvOS: tvosArm64, tvosX64, tvosSimulatorArm64
-**watchOS**: watchosArm64, watchosX64, watchosSimulatorArm64
+- watchOS: watchosArm64, watchosX64, watchosSimulatorArm64
-**MacOS**: macosX64, macosArm64
+- MacOS: macosX64, macosArm64
**Windows**: mingwX64
@@ -53,7 +57,7 @@ val supabase = createSupabaseClient(
defaultLogLevel = LogLevel.DEBUG
//Install a plugin
- install(Auth) //from gotrue-kt
+ install(Auth) //from auth-kt
}
diff --git a/Supabase/src/commonMain/kotlin/io/github/jan/supabase/PlatformTarget.kt b/Supabase/src/commonMain/kotlin/io/github/jan/supabase/PlatformTarget.kt
index 74c57051b..6cc798d57 100644
--- a/Supabase/src/commonMain/kotlin/io/github/jan/supabase/PlatformTarget.kt
+++ b/Supabase/src/commonMain/kotlin/io/github/jan/supabase/PlatformTarget.kt
@@ -1,10 +1,11 @@
+@file:Suppress("UndocumentedPublicProperty")
package io.github.jan.supabase
/**
* Represents a target platform
*/
enum class PlatformTarget {
- JVM, ANDROID, JS, WASM, IOS, WINDOWS, MACOS, TVOS, WATCHOS, LINUX;
+ JVM, ANDROID, JS, WASM_JS, IOS, WINDOWS, MACOS, TVOS, WATCHOS, LINUX;
}
/**
diff --git a/Supabase/src/commonMain/kotlin/io/github/jan/supabase/SupabaseClient.kt b/Supabase/src/commonMain/kotlin/io/github/jan/supabase/SupabaseClient.kt
index f9bbd39de..4d5a97d2f 100644
--- a/Supabase/src/commonMain/kotlin/io/github/jan/supabase/SupabaseClient.kt
+++ b/Supabase/src/commonMain/kotlin/io/github/jan/supabase/SupabaseClient.kt
@@ -14,7 +14,7 @@ import io.ktor.client.engine.HttpClientEngine
/**
* The main class to interact with Supabase.
*
- * To add functionality, add plugins like **GoTrue** or **Functions** within the [SupabaseClientBuilder]
+ * To add functionality, add plugins like **Auth** or **Functions** within the [SupabaseClientBuilder]
*/
sealed interface SupabaseClient {
@@ -85,6 +85,7 @@ sealed interface SupabaseClient {
}
+@Suppress("LongParameterList") //TODO: maybe refactor
internal class SupabaseClientImpl(
override val supabaseUrl: String,
override val supabaseKey: String,
diff --git a/Supabase/src/commonMain/kotlin/io/github/jan/supabase/network/KtorSupabaseHttpClient.kt b/Supabase/src/commonMain/kotlin/io/github/jan/supabase/network/KtorSupabaseHttpClient.kt
index a46079bf3..b86c9bf2f 100644
--- a/Supabase/src/commonMain/kotlin/io/github/jan/supabase/network/KtorSupabaseHttpClient.kt
+++ b/Supabase/src/commonMain/kotlin/io/github/jan/supabase/network/KtorSupabaseHttpClient.kt
@@ -29,6 +29,11 @@ import kotlin.time.Duration.Companion.milliseconds
private const val HTTPS_PORT = 443
+/**
+ * A function that can be used to override the default request configuration
+ */
+typealias HttpRequestOverride = HttpRequestBuilder.() -> Unit
+
/**
* A [SupabaseHttpClient] that uses ktor to send requests
*/
diff --git a/Supabase/src/commonMain/kotlin/io/github/jan/supabase/plugins/MainPlugin.kt b/Supabase/src/commonMain/kotlin/io/github/jan/supabase/plugins/MainPlugin.kt
index 7657147b0..8a8a3e1bc 100644
--- a/Supabase/src/commonMain/kotlin/io/github/jan/supabase/plugins/MainPlugin.kt
+++ b/Supabase/src/commonMain/kotlin/io/github/jan/supabase/plugins/MainPlugin.kt
@@ -18,7 +18,7 @@ open class MainConfig {
var customUrl: String? = null
/**
- * The jwt token used for this module. If null, the client will use the token from GoTrue's current session
+ * The jwt token used for this module. If null, the client will use the token from Auth's current session
*/
var jwtToken: String? = null
diff --git a/Supabase/src/wasmJsMain/kotlin/io/github/jan/supabase/PlatformTarget.wasmJs.kt b/Supabase/src/wasmJsMain/kotlin/io/github/jan/supabase/PlatformTarget.wasmJs.kt
new file mode 100644
index 000000000..ea6e549ee
--- /dev/null
+++ b/Supabase/src/wasmJsMain/kotlin/io/github/jan/supabase/PlatformTarget.wasmJs.kt
@@ -0,0 +1,6 @@
+package io.github.jan.supabase
+
+/**
+ * The current target platform
+ */
+actual val CurrentPlatformTarget: PlatformTarget = PlatformTarget.WASM_JS
\ No newline at end of file
diff --git a/Supabase/src/wasmJsTest/kotlin/PlatformTargetTest.kt b/Supabase/src/wasmJsTest/kotlin/PlatformTargetTest.kt
new file mode 100644
index 000000000..41a03d9a5
--- /dev/null
+++ b/Supabase/src/wasmJsTest/kotlin/PlatformTargetTest.kt
@@ -0,0 +1,13 @@
+import io.github.jan.supabase.CurrentPlatformTarget
+import io.github.jan.supabase.PlatformTarget
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+actual class PlatformTargetTest {
+
+ @Test
+ actual fun testPlatformTarget() {
+ assertEquals(PlatformTarget.WASM_JS, CurrentPlatformTarget, "The current platform target should be WASM_JS")
+ }
+
+}
\ No newline at end of file
diff --git a/TROUBLESHOOTING.md b/TROUBLESHOOTING.md
index 76caef3d8..2214c4ba1 100644
--- a/TROUBLESHOOTING.md
+++ b/TROUBLESHOOTING.md
@@ -77,7 +77,7 @@ Not all Ktor client [engines](https://ktor.io/docs/http-client-engines.html#limi
### Auth
-GoTrue#sessionStatus doesn't change when signing up
+Auth#sessionStatus doesn't change when signing up
If you don't have auto confirm enabled, your user has to confirm their email/phone number before they can sign in. Once they confirm, the session status will change.
On Android and iOS, you can use deeplinking to automatically sign-in when the user clicks on the confirmation link.
diff --git a/build.gradle.kts b/build.gradle.kts
index b52d74856..d090d7278 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -7,7 +7,7 @@ import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension
val excludedModules = listOf("plugins", "serializers", "test-common")
fun libraryModules(withBom: Boolean = true, init: Project.() -> Unit) = configure(
- allprojects.filter { it.name !in excludedModules && !it.path.contains("sample") && if(withBom) true else it.name != "bom" },
+ allprojects.filter { it.name !in excludedModules && !it.path.contains("sample") && if(withBom) true else it.name != "bom" && it.name != it.rootProject.name },
init
)
@@ -45,11 +45,13 @@ libraryModules {
applyPublishing()
}
-libraryModules(false) {
- apply(plugin = "io.gitlab.arturbosch.detekt")
+val reportMerge by tasks.registering(io.gitlab.arturbosch.detekt.report.ReportMergeTask::class) {
+ output.set(rootProject.layout.buildDirectory.file("reports/detekt/merge.sarif"))
+}
+libraryModules(false) {
applyDokkaWithConfiguration()
- applyDetektWithConfiguration()
+ applyDetektWithConfiguration(reportMerge)
}
tasks.register("detektAll") {
diff --git a/buildSrc/src/main/kotlin/Detekt.kt b/buildSrc/src/main/kotlin/Detekt.kt
index 00c3a22ec..ff81e7b51 100644
--- a/buildSrc/src/main/kotlin/Detekt.kt
+++ b/buildSrc/src/main/kotlin/Detekt.kt
@@ -1,12 +1,15 @@
import io.gitlab.arturbosch.detekt.DetektPlugin
import io.gitlab.arturbosch.detekt.extensions.DetektExtension
+import io.gitlab.arturbosch.detekt.report.ReportMergeTask
import org.gradle.api.Project
+import org.gradle.api.tasks.TaskProvider
import org.gradle.kotlin.dsl.apply
import org.gradle.kotlin.dsl.configure
import org.gradle.kotlin.dsl.findPlugin
+import org.gradle.kotlin.dsl.invoke
import org.gradle.kotlin.dsl.withType
-fun Project.applyDetektWithConfiguration() {
+fun Project.applyDetektWithConfiguration(reportMerge: TaskProvider) {
apply(plugin = "io.gitlab.arturbosch.detekt")
plugins.findPlugin(DetektPlugin::class)?.let {
@@ -20,12 +23,21 @@ fun Project.applyDetektWithConfiguration() {
tasks.withType().configureEach {
jvmTarget = "1.8"
reports {
- xml.required.set(true)
- html.required.set(true)
- txt.required.set(true)
+ xml.required.set(false)
+ html.required.set(false)
+ txt.required.set(false)
sarif.required.set(true)
- md.required.set(true)
+ md.required.set(false)
}
basePath = rootDir.absolutePath
}
+
+ tasks.withType().configureEach {
+ finalizedBy(reportMerge)
+ }
+
+ reportMerge.invoke {
+ input.from(tasks.withType().map { it.sarifReportFile })
+ }
+
}
\ No newline at end of file
diff --git a/buildSrc/src/main/kotlin/Dokka.kt b/buildSrc/src/main/kotlin/Dokka.kt
index d31a3e2db..65ade563b 100644
--- a/buildSrc/src/main/kotlin/Dokka.kt
+++ b/buildSrc/src/main/kotlin/Dokka.kt
@@ -7,8 +7,9 @@ fun Project.applyDokkaWithConfiguration() {
dokkaSourceSets.configureEach {
sourceLink {
val name = when(moduleName.get()) {
+ "supabase-kt" -> "Supabase"
"functions-kt" -> "Functions"
- "gotrue-kt" -> "GoTrue"
+ "auth-kt" -> "Auth"
"postgrest-kt" -> "Postgrest"
"realtime-kt" -> "Realtime"
"storage-kt" -> "Storage"
@@ -16,6 +17,7 @@ fun Project.applyDokkaWithConfiguration() {
"compose-auth" -> "plugins/ComposeAuth"
"compose-auth-ui" -> "plugins/ComposeAuthUI"
"coil-integration" -> "plugins/CoilIntegration"
+ "coil3-integration" -> "plugins/Coil3Integration"
"imageloader-integration" -> "plugins/ImageLoaderIntegration"
"serializer-moshi" -> "serializers/Moshi"
"serializer-jackson" -> "serializers/Jackson"
diff --git a/buildSrc/src/main/kotlin/KotlinPlugin.kt b/buildSrc/src/main/kotlin/KotlinPlugin.kt
index cdafe1e5d..ad9119619 100644
--- a/buildSrc/src/main/kotlin/KotlinPlugin.kt
+++ b/buildSrc/src/main/kotlin/KotlinPlugin.kt
@@ -10,5 +10,5 @@ fun KotlinMultiplatformExtension.defaultConfig() {
}
compilerOptions.freeCompilerArgs.add("-Xexpect-actual-classes")
applyDefaultHierarchyTemplate()
- jvmToolchain(8)
+ // jvmToolchain(8)
}
\ No newline at end of file
diff --git a/buildSrc/src/main/kotlin/KotlinTargets.kt b/buildSrc/src/main/kotlin/KotlinTargets.kt
index b65d5fdbe..8b6e82e7f 100644
--- a/buildSrc/src/main/kotlin/KotlinTargets.kt
+++ b/buildSrc/src/main/kotlin/KotlinTargets.kt
@@ -1,4 +1,8 @@
+import org.gradle.kotlin.dsl.assign
+import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
+import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
+import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl
fun KotlinMultiplatformExtension.iosTargets() {
iosX64()
@@ -29,13 +33,25 @@ fun KotlinMultiplatformExtension.desktopTargets() {
linuxX64()
}
-fun KotlinMultiplatformExtension.jvmTargets() {
- jvm()
+fun KotlinMultiplatformExtension.configuredJvmTarget() {
+ jvm {
+ compilerOptions.jvmTarget = JvmTarget.JVM_1_8
+ }
+}
+
+fun KotlinMultiplatformExtension.configuredAndroidTarget() {
androidTarget {
publishLibraryVariants("release", "debug")
+ compilerOptions.jvmTarget = JvmTarget.JVM_1_8
}
}
+@OptIn(ExperimentalKotlinGradlePluginApi::class)
+fun KotlinMultiplatformExtension.jvmTargets() {
+ configuredAndroidTarget()
+ configuredJvmTarget()
+}
+
fun KotlinMultiplatformExtension.jsTarget() {
js {
browser {
@@ -51,6 +67,22 @@ fun KotlinMultiplatformExtension.jsTarget() {
}
}
+fun KotlinMultiplatformExtension.wasmJsTarget() {
+ wasmJs {
+ browser {
+ testTask {
+ enabled = false
+ }
+ }
+ nodejs {
+ testTask {
+ enabled = false
+ }
+ }
+ }
+}
+
+@OptIn(ExperimentalWasmDsl::class)
fun KotlinMultiplatformExtension.allTargets() {
jvmTargets()
jsTarget()
@@ -58,10 +90,12 @@ fun KotlinMultiplatformExtension.allTargets() {
watchosTargets()
tvosTargets()
desktopTargets()
+ wasmJsTarget()
}
fun KotlinMultiplatformExtension.composeTargets() {
jvmTargets()
jsTarget()
iosTargets()
+ wasmJsTarget()
}
\ No newline at end of file
diff --git a/buildSrc/src/main/kotlin/Modules.kt b/buildSrc/src/main/kotlin/Modules.kt
index 0cdea329d..4cf5bdc56 100644
--- a/buildSrc/src/main/kotlin/Modules.kt
+++ b/buildSrc/src/main/kotlin/Modules.kt
@@ -4,7 +4,7 @@ import org.jetbrains.kotlin.gradle.plugin.KotlinDependencyHandler
enum class SupabaseModule(val module: String) {
SUPABASE("supabase-kt"),
- GOTRUE("gotrue-kt"),
+ AUTH("auth-kt"),
STORAGE("storage-kt"),
REALTIME("realtime-kt"),
FUNCTIONS("functions-kt"),
diff --git a/buildSrc/src/main/kotlin/TargetHierarchy.kt b/buildSrc/src/main/kotlin/TargetHierarchy.kt
index b152143f4..f9bb83202 100644
--- a/buildSrc/src/main/kotlin/TargetHierarchy.kt
+++ b/buildSrc/src/main/kotlin/TargetHierarchy.kt
@@ -17,5 +17,6 @@ fun KotlinHierarchyBuilder.settingsGroup() {
withJs()
withMingwX64()
withApple()
+ withWasmJs()
}
}
\ No newline at end of file
diff --git a/demos/android-login/README.md b/demos/android-login/README.md
index 8a2a312e3..b26e76ff5 100644
--- a/demos/android-login/README.md
+++ b/demos/android-login/README.md
@@ -1,10 +1,12 @@
+# Deprecation note
+This sample has been deprecated. Checkout the [official video](https://www.youtube.com/watch?v=P_jZMDmodG4&pp=ygUWc3VwYWJhc2UgZ29vZ2xlIGtvdGxpbg%3D%3D) on how to use the Google Credential Manager to natively sign in with Google or checkout [Compose Auth](/plugins/ComposeAuth)
# Android Login Demo
This is a demo of an android app using Google OneTap/Native Sign-In & WebView for authentication
**Available platforms:** Android
-**Modules used:** GoTrue
+**Modules used:** Auth
https://user-images.githubusercontent.com/26686035/235766941-6a62c415-e07e-4d18-9706-0a246e6821eb.mp4
diff --git a/demos/android-login/common/src/androidMain/kotlin/io/github/jan/supabase/common/di/viewModel.kt b/demos/android-login/common/src/androidMain/kotlin/io/github/jan/supabase/common/di/viewModel.kt
index 6964690a2..4f17e99bb 100644
--- a/demos/android-login/common/src/androidMain/kotlin/io/github/jan/supabase/common/di/viewModel.kt
+++ b/demos/android-login/common/src/androidMain/kotlin/io/github/jan/supabase/common/di/viewModel.kt
@@ -1,6 +1,5 @@
package io.github.jan.supabase.common.di
-import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.core.module.Module
actual fun Module.viewModel() {
diff --git a/demos/android-login/common/src/commonMain/kotlin/io/github/jan/supabase/common/ui/components/OAuthWebView.kt b/demos/android-login/common/src/commonMain/kotlin/io/github/jan/supabase/common/ui/components/OAuthWebView.kt
index b69f4ab4b..31c5d6854 100644
--- a/demos/android-login/common/src/commonMain/kotlin/io/github/jan/supabase/common/ui/components/OAuthWebView.kt
+++ b/demos/android-login/common/src/commonMain/kotlin/io/github/jan/supabase/common/ui/components/OAuthWebView.kt
@@ -1,13 +1,11 @@
package io.github.jan.supabase.common.ui.components
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Modifier
import com.google.accompanist.web.WebView
import com.google.accompanist.web.WebViewNavigator
import com.google.accompanist.web.WebViewState
-import io.ktor.client.plugins.UserAgent
import io.ktor.http.Url
@Composable
diff --git a/demos/android-login/common/src/commonMain/kotlin/io/github/jan/supabase/common/ui/screen/OAuthScreen.kt b/demos/android-login/common/src/commonMain/kotlin/io/github/jan/supabase/common/ui/screen/OAuthScreen.kt
index cd0f34fac..19c895edb 100644
--- a/demos/android-login/common/src/commonMain/kotlin/io/github/jan/supabase/common/ui/screen/OAuthScreen.kt
+++ b/demos/android-login/common/src/commonMain/kotlin/io/github/jan/supabase/common/ui/screen/OAuthScreen.kt
@@ -5,8 +5,6 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.Refresh
@@ -18,7 +16,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextOverflow
-import androidx.compose.ui.unit.dp
import com.google.accompanist.web.rememberWebViewNavigator
import com.google.accompanist.web.rememberWebViewState
import io.github.jan.supabase.common.ui.components.OAuthWebView
diff --git a/demos/multiplatform-deeplinks/common/src/androidMain/kotlin/io/github/jan/supabase/common/di/viewModel.kt b/demos/multiplatform-deeplinks/common/src/androidMain/kotlin/io/github/jan/supabase/common/di/viewModel.kt
index 6964690a2..4f17e99bb 100644
--- a/demos/multiplatform-deeplinks/common/src/androidMain/kotlin/io/github/jan/supabase/common/di/viewModel.kt
+++ b/demos/multiplatform-deeplinks/common/src/androidMain/kotlin/io/github/jan/supabase/common/di/viewModel.kt
@@ -1,6 +1,5 @@
package io.github.jan.supabase.common.di
-import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.core.module.Module
actual fun Module.viewModel() {
diff --git a/demos/multiplatform-deeplinks/common/src/commonMain/kotlin/io/github/jan/supabase/common/AppViewModel.kt b/demos/multiplatform-deeplinks/common/src/commonMain/kotlin/io/github/jan/supabase/common/AppViewModel.kt
index 02b67d540..9f1a45adf 100644
--- a/demos/multiplatform-deeplinks/common/src/commonMain/kotlin/io/github/jan/supabase/common/AppViewModel.kt
+++ b/demos/multiplatform-deeplinks/common/src/commonMain/kotlin/io/github/jan/supabase/common/AppViewModel.kt
@@ -1,13 +1,12 @@
package io.github.jan.supabase.common
-import co.touchlab.kermit.Logger
import io.github.jan.supabase.SupabaseClient
+import io.github.jan.supabase.annotations.SupabaseInternal
import io.github.jan.supabase.gotrue.gotrue
import io.github.jan.supabase.gotrue.parseFragmentAndImportSession
import io.github.jan.supabase.gotrue.providers.Google
import io.github.jan.supabase.gotrue.providers.builtin.Email
-import io.github.jan.supabase.annotations.SupabaseInternal
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
diff --git a/detekt.yml b/detekt.yml
index 71f38d60c..0762d3878 100644
--- a/detekt.yml
+++ b/detekt.yml
@@ -67,7 +67,7 @@ comments:
endOfSentenceFormat: '([.?!][ \t\n\r\f<])|([.?!:]$)'
KDocReferencesNonPublicProperty:
active: false
- excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**', '**/mingwX64Test/**', '**/macosTest/**', '**/appleTest/**', '**/linuxTest/**']
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**', '**/mingwX64Test/**', '**/macosTest/**', '**/appleTest/**', '**/linuxTest/**', '**/wasmJsTest/**']
OutdatedDocumentation:
active: false
matchTypeParameters: true
@@ -75,10 +75,10 @@ comments:
allowParamOnConstructorProperties: false
UndocumentedPublicClass:
active: true
- excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**', '**/mingwX64Test/**', '**/macosTest/**', '**/appleTest/**', '**/linuxTest/**']
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**', '**/mingwX64Test/**', '**/macosTest/**', '**/appleTest/**', '**/linuxTest/**', '**/wasmJsTest/**']
searchInNestedClass: true
searchInInnerClass: true
- searchInInnerObject: true
+ searchInInnerObject: false
searchInInnerInterface: true
searchInProtectedClass: false
ignoreAnnotated:
@@ -86,14 +86,14 @@ comments:
- 'SupabaseExperimental'
UndocumentedPublicFunction:
active: true
- excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**', '**/mingwX64Test/**', '**/macosTest/**', '**/appleTest/**', '**/linuxTest/**']
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**', '**/mingwX64Test/**', '**/macosTest/**', '**/appleTest/**', '**/linuxTest/**', '**/wasmJsTest/**']
searchProtectedFunction: false
ignoreAnnotated:
- 'SupabaseInternal'
- 'SupabaseExperimental'
UndocumentedPublicProperty:
active: true
- excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**', '**/mingwX64Test/**', '**/macosTest/**', '**/appleTest/**', '**/linuxTest/**']
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**', '**/mingwX64Test/**', '**/macosTest/**', '**/appleTest/**', '**/linuxTest/**', '**/wasmJsTest/**']
searchProtectedProperty: false
ignoreAnnotated:
- 'SupabaseInternal'
@@ -114,7 +114,7 @@ complexity:
includePrivateDeclarations: false
ignoreOverloaded: false
CyclomaticComplexMethod:
- active: false
+ active: true
threshold: 15
ignoreSingleWhenExpression: false
ignoreSimpleWhenEntries: false
@@ -139,12 +139,14 @@ complexity:
active: true
threshold: 70
LongParameterList:
- active: false
+ active: true
functionThreshold: 6
constructorThreshold: 7
ignoreDefaultParameters: false
ignoreDataClasses: true
ignoreAnnotatedParameter: []
+ ignoreAnnotated:
+ - 'Composable'
MethodOverloading:
active: false
threshold: 6
@@ -168,14 +170,14 @@ complexity:
active: false
StringLiteralDuplication:
active: false
- excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**', '**/mingwX64Test/**', '**/macosTest/**', '**/appleTest/**', '**/linuxTest/**']
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**', '**/mingwX64Test/**', '**/macosTest/**', '**/appleTest/**', '**/linuxTest/**', '**/wasmJsTest/**']
threshold: 3
ignoreAnnotation: true
excludeStringsWithLessThan5Characters: true
ignoreStringsRegex: '$^'
TooManyFunctions:
active: false
- excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**', '**/mingwX64Test/**', '**/macosTest/**', '**/appleTest/**', '**/linuxTest/**']
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**', '**/mingwX64Test/**', '**/macosTest/**', '**/appleTest/**', '**/linuxTest/**', '**/wasmJsTest/**']
thresholdInFiles: 11
thresholdInClasses: 11
thresholdInInterfaces: 11
@@ -188,7 +190,7 @@ complexity:
coroutines:
active: true
GlobalCoroutineUsage:
- active: false
+ active: true
InjectDispatcher:
active: false
dispatcherNames:
@@ -252,7 +254,7 @@ exceptions:
- 'toString'
InstanceOfCheckForException:
active: false
- excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**', '**/mingwX64Test/**', '**/macosTest/**', '**/appleTest/**', '**/linuxTest/**']
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**', '**/mingwX64Test/**', '**/macosTest/**', '**/appleTest/**', '**/linuxTest/**', '**/wasmJsTest/**']
NotImplementedDeclaration:
active: false
ObjectExtendsThrowable:
@@ -278,7 +280,7 @@ exceptions:
active: false
ThrowingExceptionsWithoutMessageOrCause:
active: true
- excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**', '**/mingwX64Test/**', '**/macosTest/**', '**/appleTest/**', '**/linuxTest/**']
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**', '**/mingwX64Test/**', '**/macosTest/**', '**/appleTest/**', '**/linuxTest/**', '**/wasmJsTest/**']
exceptions:
- 'ArrayIndexOutOfBoundsException'
- 'Exception'
@@ -293,7 +295,7 @@ exceptions:
active: true
TooGenericExceptionCaught:
active: false
- excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**', '**/mingwX64Test/**', '**/macosTest/**', '**/appleTest/**', '**/linuxTest/**']
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**', '**/mingwX64Test/**', '**/macosTest/**', '**/appleTest/**', '**/linuxTest/**', '**/wasmJsTest/**']
exceptionNames:
- 'ArrayIndexOutOfBoundsException'
- 'Error'
@@ -339,7 +341,7 @@ naming:
minimumFunctionNameLength: 3
FunctionNaming:
active: true
- excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**', '**/mingwX64Test/**', '**/macosTest/**', '**/appleTest/**', '**/linuxTest/**']
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**', '**/mingwX64Test/**', '**/macosTest/**', '**/appleTest/**', '**/linuxTest/**', '**/wasmJsTest/**']
functionPattern: '[a-z][a-zA-Z0-9]*'
excludeClassPattern: '$^'
ignoreAnnotated:
@@ -399,10 +401,10 @@ performance:
threshold: 3
ForEachOnRange:
active: true
- excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**', '**/mingwX64Test/**', '**/macosTest/**', '**/appleTest/**', '**/linuxTest/**']
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**', '**/mingwX64Test/**', '**/macosTest/**', '**/appleTest/**', '**/linuxTest/**', '**/wasmJsTest/**']
SpreadOperator:
active: false
- excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**', '**/mingwX64Test/**', '**/macosTest/**', '**/appleTest/**', '**/linuxTest/**']
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**', '**/mingwX64Test/**', '**/macosTest/**', '**/appleTest/**', '**/linuxTest/**', '**/wasmJsTest/**']
UnnecessaryPartOfBinaryExpression:
active: false
UnnecessaryTemporaryInstantiation:
@@ -475,7 +477,7 @@ potential-bugs:
active: true
LateinitUsage:
active: false
- excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**', '**/mingwX64Test/**', '**/macosTest/**', '**/appleTest/**', '**/linuxTest/**']
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**', '**/mingwX64Test/**', '**/macosTest/**', '**/appleTest/**', '**/linuxTest/**', '**/wasmJsTest/**']
ignoreOnClassesPattern: ''
MapGetWithNotNullAssertionOperator:
active: true
@@ -502,7 +504,7 @@ potential-bugs:
active: true
UnsafeCallOnNullableType:
active: true
- excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**', '**/mingwX64Test/**', '**/macosTest/**', '**/appleTest/**', '**/linuxTest/**']
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**', '**/mingwX64Test/**', '**/macosTest/**', '**/appleTest/**', '**/linuxTest/**', '**/wasmJsTest/**']
UnsafeCast:
active: true
UnusedUnaryOperator:
diff --git a/gradle.properties b/gradle.properties
index d55d38382..c3421909c 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -3,11 +3,12 @@ android.useAndroidX=true
org.gradle.jvmargs=-Xmx4096m
kotlin.mpp.androidSourceSetLayoutVersion=2
kotlin.native.ignoreDisabledTargets=true
-kotlin.native.ignoreIncorrectDependencies=true
org.gradle.parallel=true
+kotlin.suppressGradlePluginWarnings=IncorrectCompileOnlyDependencyWarning
org.jetbrains.compose.experimental.uikit.enabled=true
org.jetbrains.compose.experimental.jscanvas.enabled=true
+org.jetbrains.compose.experimental.wasm.enabled=true
-supabase-version = 2.6.1
+supabase-version = 3.0.0-beta-1
base-group = io.github.jan-tennert.supabase
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 24db4733d..d369f90c4 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -1,37 +1,38 @@
[versions]
-accompanist-permissions = "0.35.1-alpha"
-kotlin = "2.0.10"
-ktor = "2.3.12"
+kotlin = "2.0.20"
+accompanist-permissions = "0.36.0"
+ktor = "3.0.0-rc-1"
dokka = "1.9.20"
-kotlinx-datetime = "0.6.0"
+kotlinx-datetime = "0.6.1"
kermit = "2.0.4"
atomicfu = "0.25.0"
-coroutines = "1.8.1"
-android-lifecycle = "2.8.4"
+coroutines = "1.9.0"
+android-lifecycle = "2.8.5"
androidx-startup = "1.1.1"
-androidx-activity-compose = "1.9.1"
-multiplatform-settings = "1.1.1"
+androidx-activity-compose = "1.9.2"
+multiplatform-settings = "1.2.0"
complete-kotlin = "1.1.0"
agp = "8.3.2"
maven-publish = "0.29.0"
-apollo-kotlin = "3.8.5"
+apollo-kotlin = "4.0.0"
korlibs = "4.0.10"
-detekt = "1.23.6"
+detekt = "1.23.7"
moshi = "1.15.1"
jackson = "2.17.2"
browser = "1.8.0"
googleid = "1.1.1"
compose = "1.6.11"
androidsvg = "1.4"
-imageloader = "1.8.2"
-coil = "2.7.0"
-okio = "3.9.0"
+imageloader = "1.8.3"
+coil2 = "2.7.0"
+coil3 = "3.0.0-alpha10"
+okio = "3.9.1"
credentials = "1.2.2"
koin = "3.5.6"
androidx-core = "1.13.1"
androidx-compat = "1.7.0"
-androidx-lifecycle = "2.8.4"
-filekit = "0.7.0"
+androidx-lifecycle = "2.8.5"
+filekit = "0.8.2"
[plugins]
kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
@@ -93,7 +94,7 @@ multiplatform-settings-no-arg = { module = "com.russhwolf:multiplatform-settings
multiplatform-settings-coroutines = { module = "com.russhwolf:multiplatform-settings-coroutines", version.ref = "multiplatform-settings" }
multiplatform-settings-test = { module = "com.russhwolf:multiplatform-settings-test", version.ref = "multiplatform-settings" }
-apollo-kotlin = { module = "com.apollographql.apollo3:apollo-runtime", version.ref = "apollo-kotlin" }
+apollo-kotlin = { module = "com.apollographql.apollo:apollo-runtime", version.ref = "apollo-kotlin" }
krypto = { module = "com.soywiz.korlibs.krypto:krypto", version.ref = "korlibs" }
okio = { module = "com.squareup.okio:okio", version.ref = "okio" }
@@ -107,7 +108,9 @@ jackson-kotlin = { module = "com.fasterxml.jackson.module:jackson-module-kotlin"
androidsvg = { module = "com.caverock:androidsvg-aar", version.ref = "androidsvg" }
turbine = { module = "app.cash.turbine:turbine", version = "1.1.0" }
-coil = { module = "io.coil-kt:coil", version.ref = "coil" }
+coil3 = { module = "io.coil-kt.coil3:coil", version.ref = "coil3" }
+coil3-network-core = { module = "io.coil-kt.coil3:coil-network-core", version.ref = "coil3" }
+coil2 = { module = "io.coil-kt:coil", version.ref = "coil2" }
imageloader = { module = "io.github.qdsfdhvh:image-loader", version.ref = "imageloader" }
# Sample dependencies
@@ -116,8 +119,8 @@ koin-android = { module = "io.insert-koin:koin-android", version.ref = "koin" }
filekit-core = { module = "io.github.vinceglb:filekit-core", version.ref = "filekit" }
filekit-compose = { module = "io.github.vinceglb:filekit-compose", version.ref = "filekit" }
accompanist-permissions = { module = "com.google.accompanist:accompanist-permissions", version.ref = "accompanist-permissions" }
-coil-svg = { module = "io.coil-kt:coil-svg", version.ref = "coil" }
-coil-compose = { module = "io.coil-kt:coil-compose", version.ref = "coil" }
+coil2-svg = { module = "io.coil-kt:coil-svg", version.ref = "coil2" }
+coil2-compose = { module = "io.coil-kt:coil-compose", version.ref = "coil2" }
[bundles]
ktor-client = ["ktor-client-core", "ktor-client-content-negotiation", "ktor-json"]
@@ -125,3 +128,4 @@ multiplatform-settings = ["multiplatform-settings-no-arg", "multiplatform-settin
testing = ["kotlinx-coroutines-test", "multiplatform-settings-test", "ktor-client-mock", "kotlin-test"]
moshi = ["moshi", "moshi-kotlin"]
jackson = ["jackson", "jackson-kotlin"]
+coil3 = ["coil3", "coil3-network-core"]
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index e6441136f..a4b76b953 100644
Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index a4413138c..09523c0e5 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
diff --git a/gradlew b/gradlew
index 1aa94a426..f5feea6d6 100755
--- a/gradlew
+++ b/gradlew
@@ -15,6 +15,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
+# SPDX-License-Identifier: Apache-2.0
+#
##############################################################################
#
@@ -55,7 +57,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
-# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
@@ -84,7 +86,8 @@ done
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
-APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
+APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
+' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
diff --git a/gradlew.bat b/gradlew.bat
index 25da30dbd..9d21a2183 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -13,6 +13,8 @@
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
+@rem SPDX-License-Identifier: Apache-2.0
+@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
diff --git a/plugins/ApolloGraphQL/README.md b/plugins/ApolloGraphQL/README.md
index 264f7269a..73440032a 100644
--- a/plugins/ApolloGraphQL/README.md
+++ b/plugins/ApolloGraphQL/README.md
@@ -4,27 +4,27 @@ Extends Supabase-kt with an Apollo GraphQL Client.
Supported targets:
-| Target | **JVM** | **Android** | **JS** | **iOS** | **tvOS** | **watchOS** | **macOS** | **Windows** | **Linux** |
-| ------ | ------- | ----------- | ------ | ------- | -------- | ----------- | --------- | ----------- | --------- |
-| | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ |
+| Target | **JVM** | **Android** | **JS** | **Wasm** | **Apple** |
+|--------|---------|-------------|--------|----------|-----------|
+| Status | ✅ | ✅ | ✅ | ✅ | ✅ |
In-depth Kotlin targets
-**iOS:** iosArm64, iosSimulatorArm64, iosX64
-
**JS**: Browser, NodeJS
-**tvOS**: tvosArm64, tvosX64, tvosSimulatorArm64
+**Wasm**: wasm-js
+
+**Apple:**
-**watchOS**: watchosArm64, watchosX64, watchosSimulatorArm64
+- iOS: iosArm64, iosSimulatorArm64, iosX64
-**MacOS**: macosX64, macosArm64
+- tvOS: tvosArm64, tvosX64, tvosSimulatorArm64
-**Windows**: mingwX64
+- watchOS: watchosArm64, watchosX64, watchosSimulatorArm64
-**Linux**: linuxX64
+- MacOS: macosX64, macosArm64
diff --git a/plugins/ApolloGraphQL/build.gradle.kts b/plugins/ApolloGraphQL/build.gradle.kts
index af8f4e717..677476d97 100644
--- a/plugins/ApolloGraphQL/build.gradle.kts
+++ b/plugins/ApolloGraphQL/build.gradle.kts
@@ -18,14 +18,21 @@ kotlin {
macosTargets()
watchosArm64()
watchosSimulatorArm64()
+ wasmJsTarget()
tvosTargets()
sourceSets {
val commonMain by getting {
dependencies {
- addModules(SupabaseModule.GOTRUE, SupabaseModule.SUPABASE)
+ addModules(SupabaseModule.AUTH, SupabaseModule.SUPABASE)
api(libs.apollo.kotlin)
}
}
+ val commonTest by getting {
+ dependencies {
+ implementation(libs.bundles.testing)
+ implementation(project(":test-common"))
+ }
+ }
}
}
diff --git a/plugins/ApolloGraphQL/src/commonMain/kotlin/io/github/jan/supabase/graphql/ApolloHttpInterceptor.kt b/plugins/ApolloGraphQL/src/commonMain/kotlin/io/github/jan/supabase/graphql/ApolloHttpInterceptor.kt
new file mode 100644
index 000000000..608d37fd7
--- /dev/null
+++ b/plugins/ApolloGraphQL/src/commonMain/kotlin/io/github/jan/supabase/graphql/ApolloHttpInterceptor.kt
@@ -0,0 +1,26 @@
+package io.github.jan.supabase.graphql
+
+import com.apollographql.apollo.network.http.HttpInterceptor
+import com.apollographql.apollo.network.http.HttpInterceptorChain
+import io.github.jan.supabase.SupabaseClient
+import io.github.jan.supabase.auth.resolveAccessToken
+import io.github.jan.supabase.logging.d
+import io.ktor.http.HttpHeaders
+import com.apollographql.apollo.api.http.HttpRequest as ApolloHttpRequest
+import com.apollographql.apollo.api.http.HttpResponse as ApolloHttpResponse
+
+internal class ApolloHttpInterceptor(private val supabaseClient: SupabaseClient, private val config: GraphQL.Config) : HttpInterceptor {
+
+ override suspend fun intercept(
+ request: ApolloHttpRequest,
+ chain: HttpInterceptorChain
+ ): ApolloHttpResponse {
+ GraphQL.logger.d { "Intercepting Apollo request with url ${request.url}" }
+ val accessToken = supabaseClient.resolveAccessToken(config.jwtToken) ?: error("Access token should not be null")
+ val newRequest = request.newBuilder().apply {
+ addHeader(HttpHeaders.Authorization, "Bearer $accessToken")
+ }
+ return chain.proceed(newRequest.build())
+ }
+
+}
diff --git a/plugins/ApolloGraphQL/src/commonMain/kotlin/io/github/jan/supabase/graphql/GraphQL.kt b/plugins/ApolloGraphQL/src/commonMain/kotlin/io/github/jan/supabase/graphql/GraphQL.kt
index cf6d88508..b6f752ba6 100644
--- a/plugins/ApolloGraphQL/src/commonMain/kotlin/io/github/jan/supabase/graphql/GraphQL.kt
+++ b/plugins/ApolloGraphQL/src/commonMain/kotlin/io/github/jan/supabase/graphql/GraphQL.kt
@@ -1,21 +1,14 @@
package io.github.jan.supabase.graphql
-import com.apollographql.apollo3.ApolloClient
-import com.apollographql.apollo3.network.http.HttpInterceptor
-import com.apollographql.apollo3.network.http.HttpInterceptorChain
+import com.apollographql.apollo.ApolloClient
import io.github.jan.supabase.BuildConfig
import io.github.jan.supabase.SupabaseClient
import io.github.jan.supabase.exceptions.RestException
-import io.github.jan.supabase.gotrue.Auth
import io.github.jan.supabase.logging.SupabaseLogger
-import io.github.jan.supabase.logging.d
import io.github.jan.supabase.plugins.MainConfig
import io.github.jan.supabase.plugins.MainPlugin
import io.github.jan.supabase.plugins.SupabasePluginProvider
import io.ktor.client.statement.HttpResponse
-import io.ktor.http.HttpHeaders
-import com.apollographql.apollo3.api.http.HttpRequest as ApolloHttpRequest
-import com.apollographql.apollo3.api.http.HttpResponse as ApolloHttpResponse
/**
* Adds an Apollo GraphQL client to supabase-kt with all necessary headers automatically managed.
@@ -78,7 +71,7 @@ internal class GraphQLImpl(override val config: GraphQL.Config, override val sup
serverUrl(config.customUrl ?: resolveUrl())
addHttpHeader("apikey", supabaseClient.supabaseKey)
addHttpHeader("X-Client-Info", "supabase-kt/${BuildConfig.PROJECT_VERSION}")
- addHttpInterceptor(ApolloHttpInterceptor())
+ addHttpInterceptor(ApolloHttpInterceptor(supabaseClient, config))
apply(config.apolloConfiguration)
}.build()
@@ -86,21 +79,6 @@ internal class GraphQLImpl(override val config: GraphQL.Config, override val sup
throw UnsupportedOperationException("Use apolloClient for GraphQL requests")
}
- inner class ApolloHttpInterceptor: HttpInterceptor {
- override suspend fun intercept(
- request: ApolloHttpRequest,
- chain: HttpInterceptorChain
- ): ApolloHttpResponse {
- GraphQL.logger.d { "Intercepting Apollo request with url ${request.url}" }
- val accessToken = config.jwtToken ?: supabaseClient.pluginManager.getPluginOrNull(Auth)?.currentAccessTokenOrNull() ?: supabaseClient.supabaseKey
- val newRequest = request.newBuilder().apply {
- addHeader(HttpHeaders.Authorization, "Bearer $accessToken")
- }
- return chain.proceed(newRequest.build())
- }
-
- }
-
}
diff --git a/plugins/ApolloGraphQL/src/commonTest/kotlin/ApolloHttpInterceptorTest.kt b/plugins/ApolloGraphQL/src/commonTest/kotlin/ApolloHttpInterceptorTest.kt
new file mode 100644
index 000000000..27d500de5
--- /dev/null
+++ b/plugins/ApolloGraphQL/src/commonTest/kotlin/ApolloHttpInterceptorTest.kt
@@ -0,0 +1,92 @@
+import com.apollographql.apollo.annotations.ApolloExperimental
+import com.apollographql.apollo.api.http.HttpMethod
+import com.apollographql.apollo.api.http.HttpRequest
+import com.apollographql.apollo.api.http.HttpResponse
+import com.apollographql.apollo.api.http.get
+import com.apollographql.apollo.network.http.HttpInterceptorChain
+import io.github.jan.supabase.SupabaseClient
+import io.github.jan.supabase.auth.Auth
+import io.github.jan.supabase.auth.auth
+import io.github.jan.supabase.auth.minimalSettings
+import io.github.jan.supabase.graphql.ApolloHttpInterceptor
+import io.github.jan.supabase.graphql.GraphQL
+import io.github.jan.supabase.graphql.graphql
+import io.github.jan.supabase.testing.createMockedSupabaseClient
+import kotlinx.coroutines.test.runTest
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+class ApolloHttpInterceptorTest {
+
+ @Test
+ fun testApolloHttpInterceptorWithApiKey() {
+ runTest {
+ val supabaseClient = createMockedSupabaseClient(
+ supabaseKey = "testkey",
+ configuration = {
+ install(GraphQL)
+ }
+ )
+ testInterceptor(supabaseClient, "testkey")
+ }
+ }
+
+ @Test
+ fun testApolloHttpInterceptorWithAuthToken() {
+ runTest {
+ val supabaseClient = createMockedSupabaseClient(
+ configuration = {
+ install(GraphQL)
+ install(Auth) {
+ minimalSettings()
+ }
+ }
+ )
+ supabaseClient.auth.importAuthToken("testtoken")
+ testInterceptor(supabaseClient, "testtoken")
+ }
+ }
+
+ @Test
+ fun testApolloHttpInterceptorWithAccessToken() {
+ runTest {
+ val supabaseClient = createMockedSupabaseClient(
+ configuration = {
+ install(GraphQL)
+ accessToken = {
+ "testtoken"
+ }
+ }
+ )
+ testInterceptor(supabaseClient, "testtoken")
+ }
+ }
+
+ @Test
+ fun testApolloHttpInterceptorWithACustomToken() {
+ runTest {
+ val supabaseClient = createMockedSupabaseClient(
+ configuration = {
+ install(GraphQL) {
+ jwtToken = "testtoken"
+ }
+ }
+ )
+ testInterceptor(supabaseClient, "testtoken")
+ }
+ }
+
+ @OptIn(ApolloExperimental::class)
+ private suspend fun testInterceptor(supabaseClient: SupabaseClient, token: String) {
+ val interceptor = ApolloHttpInterceptor(supabaseClient, supabaseClient.graphql.config)
+ interceptor.intercept(HttpRequest.Builder(HttpMethod.Get, "").build(), object : HttpInterceptorChain {
+
+ override suspend fun proceed(request: HttpRequest): HttpResponse {
+ assertEquals("Bearer $token", request.headers.get("Authorization"))
+ return HttpResponse.Builder(200).build()
+ }
+
+ })
+ }
+
+}
diff --git a/plugins/ApolloGraphQL/src/commonTest/kotlin/GraphQLTest.kt b/plugins/ApolloGraphQL/src/commonTest/kotlin/GraphQLTest.kt
new file mode 100644
index 000000000..bc9baf677
--- /dev/null
+++ b/plugins/ApolloGraphQL/src/commonTest/kotlin/GraphQLTest.kt
@@ -0,0 +1,37 @@
+import com.apollographql.apollo.api.http.HttpHeader
+import io.github.jan.supabase.BuildConfig
+import io.github.jan.supabase.graphql.ApolloHttpInterceptor
+import io.github.jan.supabase.graphql.GraphQL
+import io.github.jan.supabase.graphql.graphql
+import io.github.jan.supabase.testing.createMockedSupabaseClient
+import kotlin.test.Test
+import kotlin.test.assertContains
+import kotlin.test.assertEquals
+import kotlin.test.assertNotNull
+import kotlin.test.assertTrue
+
+class GraphQLTest {
+
+ @Test
+ fun testCreatedGraphQLClient() {
+ val supabaseClient = createMockedSupabaseClient(
+ supabaseUrl = "https://test.supabase.co",
+ supabaseKey = "testkey",
+ configuration = {
+ install(GraphQL) {
+ apolloConfiguration {
+ webSocketIdleTimeoutMillis(1000)
+ }
+ }
+ }
+ )
+ val client = supabaseClient.graphql.apolloClient
+ assertEquals("https://test.supabase.co/graphql/v1", client.newBuilder().httpServerUrl)
+ assertNotNull(client.httpHeaders)
+ assertContains(client.httpHeaders!!, HttpHeader("apikey", "testkey"))
+ assertContains(client.httpHeaders!!, HttpHeader("X-Client-Info", "supabase-kt/${BuildConfig.PROJECT_VERSION}"))
+ assertTrue { client.newBuilder().httpInterceptors.any { it is ApolloHttpInterceptor } }
+ assertEquals(1000, client.newBuilder().webSocketIdleTimeoutMillis)
+ }
+
+}
\ No newline at end of file
diff --git a/plugins/Coil3Integration/README.md b/plugins/Coil3Integration/README.md
new file mode 100644
index 000000000..35b3389aa
--- /dev/null
+++ b/plugins/Coil3Integration/README.md
@@ -0,0 +1,105 @@
+# Supabase-kt Coil 3 Integration
+
+Extends supabase-kt with a Coil3 integration for image loading.
+**Requires supabase-kt `3.0.0` or higher.**
+
+Current supported Coil3 version: `3.0.0-alpha10`
+
+Supported targets:
+
+| Target | **JVM** | **Android** | **JS** | **Wasm** | **iOS** |
+|--------|---------|-------------|--------|----------|---------|
+| Status | ✅ | ✅ | ✅ | ✅ | ✅ |
+
+
+
+In-depth Kotlin targets
+
+**JS**: Browser
+
+**Wasm**: wasm-js
+
+**iOS**: iosArm64, iosSimulatorArm64, iosX64
+
+
+
+# Installation
+
+Newest version: [![](https://img.shields.io/github/release/supabase-community/supabase-kt?label=)](https://github.com/supabase-community/supabase-kt/releases)
+
+```kotlin
+dependencies {
+ implementation("io.github.jan-tennert.supabase:coil3-integration:VERSION")
+}
+```
+
+Install plugin in main SupabaseClient. See the [documentation](https://supabase.com/docs/reference/kotlin/initializing) for more information
+```kotlin
+val client = createSupabaseClient(
+ supabaseUrl = "https://id.supabase.co",
+ supabaseKey = "apikey"
+) {
+ //...
+ install(Storage) {
+ //your config
+ }
+ install(Coil3Integration)
+}
+```
+
+If you don't have a coil-network artifact in your dependencies, you will need to add it. See the [Coil documentation](https://coil-kt.github.io/coil/upgrading_to_coil3/#network-images) for more information.
+
+# Usage
+
+### Add Supabase Fetcher to Coil
+
+Create a new ImageLoader with the Supabase Fetcher and a [network fetcher](https://coil-kt.github.io/coil/upgrading_to_coil3/#network-images).
+
+```kotlin
+ImageLoader.Builder(context)
+ .components {
+ add(supabaseClient.coil3)
+ //You also need the add the network fetcher factory if you don't have it already
+ //Depending on the network artifact you added, this will be different
+ add(KtorNetworkFetcherFactory())
+ }
+ .build()
+```
+
+You can also replace the default Coil Image Loader in your application.
+For Compose Multiplatform Applications using the `coil-compose` dependency, you can use the `setSingletonImageLoaderFactory` composable function:
+```kotlin
+setSingletonImageLoaderFactory { platformContext ->
+ ImageLoader.Builder(platformContext)
+ .components {
+ add(supabaseClient.coil3)
+ //Your network fetcher factory
+ add(KtorNetworkFetcherFactory())
+ }
+ .build()
+}
+```
+You call this composable before any `Image` composable is used. Presumably in your `Root` composable.
+
+See the [Coil documentation](https://coil-kt.github.io/coil/getting_started/#image-loaders) for more information.
+
+### Display images from Supabase Storage
+
+You can easily create an image request like this:
+
+```kotlin
+val request = ImageRequest.Builder(context)
+ .data(authenticatedStorageItem("icons", "profile.png")) //for non-public buckets
+ .build()
+```
+
+Or if you are using [Compose Multiplatform](https://coil-kt.github.io/coil/compose/):
+
+```kotlin
+AsyncImage(
+ model = publicStorageItem("icons", "profile.png"), //for public buckets
+ contentDescription = null,
+)
+```
+
+The Coil integration will automatically add the Authorization header to the request if the bucket is not public.
\ No newline at end of file
diff --git a/plugins/Coil3Integration/build.gradle.kts b/plugins/Coil3Integration/build.gradle.kts
new file mode 100644
index 000000000..9ef71811a
--- /dev/null
+++ b/plugins/Coil3Integration/build.gradle.kts
@@ -0,0 +1,27 @@
+plugins {
+ id(libs.plugins.kotlin.multiplatform.get().pluginId)
+ id(libs.plugins.android.library.get().pluginId)
+}
+
+description = "Extends supabase-kt with a Coil3 integration for easy image loading"
+
+repositories {
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
+}
+
+@OptIn(org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi::class)
+kotlin {
+ defaultConfig()
+ composeTargets()
+ sourceSets {
+ val commonMain by getting {
+ dependencies {
+ api(project(":storage-kt"))
+ api(libs.bundles.coil3)
+ }
+ }
+ }
+}
+
+configureLibraryAndroidTarget()
\ No newline at end of file
diff --git a/plugins/Coil3Integration/src/androidMain/AndroidManifest.xml b/plugins/Coil3Integration/src/androidMain/AndroidManifest.xml
new file mode 100644
index 000000000..1d26c87a1
--- /dev/null
+++ b/plugins/Coil3Integration/src/androidMain/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/plugins/Coil3Integration/src/commonMain/kotlin/io/github/jan/supabase/coil/Coil3Integration.kt b/plugins/Coil3Integration/src/commonMain/kotlin/io/github/jan/supabase/coil/Coil3Integration.kt
new file mode 100644
index 000000000..f6a740412
--- /dev/null
+++ b/plugins/Coil3Integration/src/commonMain/kotlin/io/github/jan/supabase/coil/Coil3Integration.kt
@@ -0,0 +1,61 @@
+package io.github.jan.supabase.coil
+
+import coil3.ImageLoader
+import coil3.fetch.Fetcher
+import coil3.request.ImageRequest
+import coil3.request.Options
+import io.github.jan.supabase.SupabaseClient
+import io.github.jan.supabase.annotations.SupabaseExperimental
+import io.github.jan.supabase.logging.SupabaseLogger
+import io.github.jan.supabase.logging.d
+import io.github.jan.supabase.plugins.SupabasePlugin
+import io.github.jan.supabase.plugins.SupabasePluginProvider
+import io.github.jan.supabase.storage.StorageItem
+import io.github.jan.supabase.storage.storage
+
+/**
+ * A plugin that implements [Fetcher.Factory] to support using [StorageItem] as data when creating an [ImageRequest] or using it as a model in Compose Multiplatform.
+ */
+interface Coil3Integration: SupabasePlugin, Fetcher.Factory {
+
+ /**
+ * The configuration for the coil integration.
+ */
+ class Config
+
+ companion object : SupabasePluginProvider {
+
+ override val key = "coil3"
+
+ override val logger: SupabaseLogger = SupabaseClient.createLogger("Supabase-Coil3Integration")
+
+ override fun create(supabaseClient: SupabaseClient, config: Config): Coil3Integration {
+ return Coil3IntegrationImpl(supabaseClient, config)
+ }
+
+ override fun createConfig(init: Config.() -> Unit): Config {
+ return Config().apply(init)
+ }
+
+ }
+
+}
+
+internal class Coil3IntegrationImpl(
+ override val supabaseClient: SupabaseClient,
+ override val config: Coil3Integration.Config
+) : Coil3Integration {
+
+ override fun create(data: StorageItem, options: Options, imageLoader: ImageLoader): Fetcher {
+ Coil3Integration.logger.d { "Creating Storage Fetcher" }
+ return SupabaseStorageFetcher(supabaseClient.storage, data, options, imageLoader)
+ }
+
+}
+
+/**
+ * With the [Coil3Integration] plugin installed, you can use this property to access the coil fetcher factory.
+ */
+@SupabaseExperimental
+val SupabaseClient.coil3: Coil3Integration
+ get() = pluginManager.getPlugin(Coil3Integration)
\ No newline at end of file
diff --git a/plugins/Coil3Integration/src/commonMain/kotlin/io/github/jan/supabase/coil/SupabaseStorageFetcher.kt b/plugins/Coil3Integration/src/commonMain/kotlin/io/github/jan/supabase/coil/SupabaseStorageFetcher.kt
new file mode 100644
index 000000000..b1d4d8d6e
--- /dev/null
+++ b/plugins/Coil3Integration/src/commonMain/kotlin/io/github/jan/supabase/coil/SupabaseStorageFetcher.kt
@@ -0,0 +1,46 @@
+package io.github.jan.supabase.coil
+
+import coil3.Extras
+import coil3.ImageLoader
+import coil3.annotation.ExperimentalCoilApi
+import coil3.fetch.FetchResult
+import coil3.fetch.Fetcher
+import coil3.network.httpHeaders
+import coil3.request.Options
+import coil3.toUri
+import io.github.jan.supabase.logging.d
+import io.github.jan.supabase.storage.Storage
+import io.github.jan.supabase.storage.StorageItem
+import io.github.jan.supabase.storage.authenticatedRequest
+
+internal class SupabaseStorageFetcher(
+ private val storage: Storage,
+ private val item: StorageItem,
+ private val options: Options,
+ private val imageLoader: ImageLoader
+) : Fetcher {
+
+ @OptIn(ExperimentalCoilApi::class)
+ override suspend fun fetch(): FetchResult? {
+ Coil3Integration.logger.d { "Received fetcher request for item $item" }
+ val bucket = storage[item.bucketId]
+ val (token, url) = if (item.authenticated) {
+ bucket.authenticatedRequest(item.path)
+ } else {
+ null to bucket.publicUrl(item.path)
+ }
+ val extras = options.extras.newBuilder()
+ if (item.authenticated) {
+ extras[Extras.Key.httpHeaders] = options.httpHeaders.newBuilder().apply {
+ set("Authorization", "Bearer $token")
+ }.build()
+ }
+ val (fetcher, _) = imageLoader.components.newFetcher(
+ url.toUri(),
+ options.copy(extras = extras.build()),
+ imageLoader
+ ) ?: error("No fetcher found for $url")
+ return fetcher.fetch()
+ }
+
+}
\ No newline at end of file
diff --git a/plugins/CoilIntegration/README.md b/plugins/CoilIntegration/README.md
index bccdf6fcf..fe6a67644 100644
--- a/plugins/CoilIntegration/README.md
+++ b/plugins/CoilIntegration/README.md
@@ -4,29 +4,11 @@ Extends supabase-kt with a Coil integration for image loading.
Supported targets:
-| Target | **JVM** | **Android** | **JS** | **iOS** | **tvOS** | **watchOS** | **macOS** | **Windows** | **Linux** |
-|--------|---------|-------------|--------|---------|----------|-------------|-----------|-------------|-----------|
-| Status | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
+| Target | **Android** |
+|--------|-------------|
+| Status | ✅ |
-
-
-In-depth Kotlin targets
-
-**iOS:** iosArm64, iosSimulatorArm64, iosX64
-
-**JS**: Browser, NodeJS
-
-**tvOS**: tvosArm64, tvosX64, tvosSimulatorArm64
-
-**watchOS**: watchosArm64, watchosX64, watchosSimulatorArm64
-
-**MacOS**: macosX64, macosArm64
-
-**Windows**: mingwX64
-
-**Linux**: linuxX64
-
-
+For Compose Multiplatform support, [checkout the Coil 3 integration](/plugins/Coil3Integration)
# Installation
diff --git a/plugins/CoilIntegration/build.gradle.kts b/plugins/CoilIntegration/build.gradle.kts
index aba4ed921..08b436180 100644
--- a/plugins/CoilIntegration/build.gradle.kts
+++ b/plugins/CoilIntegration/build.gradle.kts
@@ -13,14 +13,12 @@ repositories {
@OptIn(org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi::class)
kotlin {
defaultConfig()
- androidTarget {
- publishLibraryVariants("release", "debug")
- }
+ configuredAndroidTarget()
sourceSets {
val commonMain by getting {
dependencies {
api(project(":storage-kt"))
- api(libs.coil)
+ api(libs.coil2)
}
}
}
diff --git a/plugins/CoilIntegration/src/commonMain/kotlin/io/github/jan/supabase/coil/CoilIntegration.kt b/plugins/CoilIntegration/src/commonMain/kotlin/io/github/jan/supabase/coil/CoilIntegration.kt
index f64f0a8ad..bc35f0ff0 100644
--- a/plugins/CoilIntegration/src/commonMain/kotlin/io/github/jan/supabase/coil/CoilIntegration.kt
+++ b/plugins/CoilIntegration/src/commonMain/kotlin/io/github/jan/supabase/coil/CoilIntegration.kt
@@ -14,7 +14,7 @@ import io.github.jan.supabase.storage.StorageItem
import io.github.jan.supabase.storage.storage
/**
- * A plugin that implements [Fetcher.Factory] to support using [StorageItem] as data when creating a [ImageRequest] or using it as a model in Jetpack Compose.
+ * A plugin that implements [Fetcher.Factory] to support using [StorageItem] as data when creating an [ImageRequest] or using it as a model in Jetpack Compose.
*/
interface CoilIntegration: SupabasePlugin, Fetcher.Factory {
diff --git a/plugins/ComposeAuth/README.md b/plugins/ComposeAuth/README.md
index ccc42b1c2..eea2ad884 100644
--- a/plugins/ComposeAuth/README.md
+++ b/plugins/ComposeAuth/README.md
@@ -4,31 +4,21 @@ Extends gotrue-kt with Native Auth composables for Compose Multiplatform
Supported targets:
-| Target | **JVM** | **Android** | **JS** | **iOS** | **tvOS** | **watchOS** | **macOS** | **Windows** | **Linux** |
-| ------ | ------- | ----------- | ------ | ------- | -------- | ----------- | --------- | ----------- | --------- |
-| | ☑️ | ✅ | ☑️ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
+| Target | **JVM** | **Android** | **JS** | **Wasm** | **iOS** |
+|--------|---------|-------------|--------|----------|---------|
+| Status | ✅ | ✅ | ✅ | ✅ | ✅ |
-> Note: iOS support is experimental and needs feedback
->
-> ☑️ = Has no support for neither Google nor Apple Native Auth, relies on gotrue-kt for OAuth.
+> Native Google Auth is only supported on Android and Native Apple Auth is only supported on iOS. Other targets or combinations rely on `gotrue-kt` for OAuth.
In-depth Kotlin targets
-**iOS:** iosArm64, iosSimulatorArm64, iosX64
+**JS**: Browser
-**JS**: Browser, NodeJS
+**Wasm**: wasm-js
-**tvOS**: tvosArm64, tvosX64, tvosSimulatorArm64
-
-**watchOS**: watchosArm64, watchosX64, watchosSimulatorArm64
-
-**MacOS**: macosX64, macosArm64
-
-**Windows**: mingwX64
-
-**Linux**: linuxX64
+**iOS**: iosArm64, iosSimulatorArm64, iosX64
diff --git a/plugins/ComposeAuth/build.gradle.kts b/plugins/ComposeAuth/build.gradle.kts
index 74ee169cb..527f4bc69 100644
--- a/plugins/ComposeAuth/build.gradle.kts
+++ b/plugins/ComposeAuth/build.gradle.kts
@@ -24,6 +24,7 @@ kotlin {
group("noDefault") {
withJvm()
withJs()
+ withWasmJs()
}
}
}
@@ -31,7 +32,7 @@ kotlin {
sourceSets {
val commonMain by getting {
dependencies {
- addModules(SupabaseModule.GOTRUE)
+ addModules(SupabaseModule.AUTH)
implementation(compose.runtime)
implementation(libs.krypto)
}
diff --git a/plugins/ComposeAuth/src/commonMain/kotlin/io/github/jan/supabase/compose/auth/ComposeAuth.kt b/plugins/ComposeAuth/src/commonMain/kotlin/io/github/jan/supabase/compose/auth/ComposeAuth.kt
index 199d84dcd..1b0b9f18d 100644
--- a/plugins/ComposeAuth/src/commonMain/kotlin/io/github/jan/supabase/compose/auth/ComposeAuth.kt
+++ b/plugins/ComposeAuth/src/commonMain/kotlin/io/github/jan/supabase/compose/auth/ComposeAuth.kt
@@ -2,13 +2,13 @@ package io.github.jan.supabase.compose.auth
import io.github.jan.supabase.SupabaseClient
import io.github.jan.supabase.SupabaseSerializer
+import io.github.jan.supabase.auth.SessionStatus
+import io.github.jan.supabase.auth.auth
+import io.github.jan.supabase.auth.providers.Apple
+import io.github.jan.supabase.auth.providers.Google
+import io.github.jan.supabase.auth.providers.IDTokenProvider
+import io.github.jan.supabase.auth.providers.builtin.IDToken
import io.github.jan.supabase.compose.auth.composable.NativeSignInState
-import io.github.jan.supabase.gotrue.SessionStatus
-import io.github.jan.supabase.gotrue.auth
-import io.github.jan.supabase.gotrue.providers.Apple
-import io.github.jan.supabase.gotrue.providers.Google
-import io.github.jan.supabase.gotrue.providers.IDTokenProvider
-import io.github.jan.supabase.gotrue.providers.builtin.IDToken
import io.github.jan.supabase.logging.SupabaseLogger
import io.github.jan.supabase.logging.d
import io.github.jan.supabase.plugins.CustomSerializationConfig
diff --git a/plugins/ComposeAuth/src/commonMain/kotlin/io/github/jan/supabase/compose/auth/composable/NativeAppleAuth.kt b/plugins/ComposeAuth/src/commonMain/kotlin/io/github/jan/supabase/compose/auth/composable/NativeAppleAuth.kt
index 307801b66..71655a27b 100644
--- a/plugins/ComposeAuth/src/commonMain/kotlin/io/github/jan/supabase/compose/auth/composable/NativeAppleAuth.kt
+++ b/plugins/ComposeAuth/src/commonMain/kotlin/io/github/jan/supabase/compose/auth/composable/NativeAppleAuth.kt
@@ -1,9 +1,9 @@
package io.github.jan.supabase.compose.auth.composable
import androidx.compose.runtime.Composable
+import io.github.jan.supabase.auth.providers.Apple
import io.github.jan.supabase.compose.auth.ComposeAuth
import io.github.jan.supabase.compose.auth.fallbackLogin
-import io.github.jan.supabase.gotrue.providers.Apple
/**
* Composable function that implements Native Apple Auth.
diff --git a/plugins/ComposeAuth/src/commonMain/kotlin/io/github/jan/supabase/compose/auth/composable/NativeGoogleAuth.kt b/plugins/ComposeAuth/src/commonMain/kotlin/io/github/jan/supabase/compose/auth/composable/NativeGoogleAuth.kt
index da45306dd..8fbb4899d 100644
--- a/plugins/ComposeAuth/src/commonMain/kotlin/io/github/jan/supabase/compose/auth/composable/NativeGoogleAuth.kt
+++ b/plugins/ComposeAuth/src/commonMain/kotlin/io/github/jan/supabase/compose/auth/composable/NativeGoogleAuth.kt
@@ -1,9 +1,9 @@
package io.github.jan.supabase.compose.auth.composable
import androidx.compose.runtime.Composable
+import io.github.jan.supabase.auth.providers.Google
import io.github.jan.supabase.compose.auth.ComposeAuth
import io.github.jan.supabase.compose.auth.fallbackLogin
-import io.github.jan.supabase.gotrue.providers.Google
/**
* Composable function that implements Native Google Auth.
diff --git a/plugins/ComposeAuthUI/README.md b/plugins/ComposeAuthUI/README.md
index 8a6575f72..8d5dbc2fb 100644
--- a/plugins/ComposeAuthUI/README.md
+++ b/plugins/ComposeAuthUI/README.md
@@ -4,27 +4,19 @@ Extends Supabase-kt with UI composables
Supported targets:
-| Target | **JVM** | **Android** | **JS** | **iOS** | **tvOS** | **watchOS** | **macOS** | **Windows** | **Linux** |
-|--------|---------|-------------|--------|---------|----------|-------------|-----------|-------------|-----------|
-| | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
+| Target | **JVM** | **Android** | **JS** | **Wasm** | **iOS** |
+|--------|---------|-------------|--------|----------|---------|
+| Status | ✅ | ✅ | ✅ | ✅ | ✅ |
In-depth Kotlin targets
-**iOS:** iosArm64, iosSimulatorArm64, iosX64
+**JS**: Browser
-**JS**: Browser, NodeJS
+**Wasm**: wasm-js
-**tvOS**: tvosArm64, tvosX64, tvosSimulatorArm64
-
-**watchOS**: watchosArm64, watchosX64, watchosSimulatorArm64
-
-**MacOS**: macosX64, macosArm64
-
-**Windows**: mingwX64
-
-**Linux**: linuxX64
+**iOS**: iosArm64, iosSimulatorArm64, iosX64
diff --git a/plugins/ComposeAuthUI/build.gradle.kts b/plugins/ComposeAuthUI/build.gradle.kts
index ea5e72dfa..7e83ad6ed 100644
--- a/plugins/ComposeAuthUI/build.gradle.kts
+++ b/plugins/ComposeAuthUI/build.gradle.kts
@@ -23,6 +23,7 @@ kotlin {
group("nonJvm") {
withIos()
withJs()
+ withWasmJs()
}
}
}
@@ -31,7 +32,7 @@ kotlin {
val commonMain by getting {
dependencies {
api(compose.ui)
- addModules(SupabaseModule.GOTRUE)
+ addModules(SupabaseModule.AUTH)
implementation(compose.material3)
}
}
diff --git a/plugins/ComposeAuthUI/src/commonMain/kotlin/io/github/jan/supabase/compose/auth/ui/ProviderButton.kt b/plugins/ComposeAuthUI/src/commonMain/kotlin/io/github/jan/supabase/compose/auth/ui/ProviderButton.kt
index 4fb738497..abaf0d7eb 100644
--- a/plugins/ComposeAuthUI/src/commonMain/kotlin/io/github/jan/supabase/compose/auth/ui/ProviderButton.kt
+++ b/plugins/ComposeAuthUI/src/commonMain/kotlin/io/github/jan/supabase/compose/auth/ui/ProviderButton.kt
@@ -11,8 +11,8 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.dp
+import io.github.jan.supabase.auth.providers.OAuthProvider
import io.github.jan.supabase.compose.auth.ui.annotations.AuthUiExperimental
-import io.github.jan.supabase.gotrue.providers.OAuthProvider
internal val DEFAULT_ICON_SIZE = 24.dp //from Material3
diff --git a/plugins/ComposeAuthUI/src/commonMain/kotlin/io/github/jan/supabase/compose/auth/ui/SvgPainter.kt b/plugins/ComposeAuthUI/src/commonMain/kotlin/io/github/jan/supabase/compose/auth/ui/SvgPainter.kt
index c45b4ee38..21218df44 100644
--- a/plugins/ComposeAuthUI/src/commonMain/kotlin/io/github/jan/supabase/compose/auth/ui/SvgPainter.kt
+++ b/plugins/ComposeAuthUI/src/commonMain/kotlin/io/github/jan/supabase/compose/auth/ui/SvgPainter.kt
@@ -6,31 +6,32 @@ import androidx.compose.runtime.produceState
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.unit.Density
import io.github.jan.supabase.annotations.SupabaseInternal
-import io.github.jan.supabase.gotrue.providers.Apple
-import io.github.jan.supabase.gotrue.providers.Azure
-import io.github.jan.supabase.gotrue.providers.Bitbucket
-import io.github.jan.supabase.gotrue.providers.Discord
-import io.github.jan.supabase.gotrue.providers.Facebook
-import io.github.jan.supabase.gotrue.providers.Figma
-import io.github.jan.supabase.gotrue.providers.Github
-import io.github.jan.supabase.gotrue.providers.Gitlab
-import io.github.jan.supabase.gotrue.providers.Google
-import io.github.jan.supabase.gotrue.providers.Kakao
-import io.github.jan.supabase.gotrue.providers.Keycloak
-import io.github.jan.supabase.gotrue.providers.LinkedIn
-import io.github.jan.supabase.gotrue.providers.Notion
-import io.github.jan.supabase.gotrue.providers.OAuthProvider
-import io.github.jan.supabase.gotrue.providers.Slack
-import io.github.jan.supabase.gotrue.providers.Spotify
-import io.github.jan.supabase.gotrue.providers.Twitch
-import io.github.jan.supabase.gotrue.providers.Twitter
-import io.github.jan.supabase.gotrue.providers.WorkOS
+import io.github.jan.supabase.auth.providers.Apple
+import io.github.jan.supabase.auth.providers.Azure
+import io.github.jan.supabase.auth.providers.Bitbucket
+import io.github.jan.supabase.auth.providers.Discord
+import io.github.jan.supabase.auth.providers.Facebook
+import io.github.jan.supabase.auth.providers.Figma
+import io.github.jan.supabase.auth.providers.Github
+import io.github.jan.supabase.auth.providers.Gitlab
+import io.github.jan.supabase.auth.providers.Google
+import io.github.jan.supabase.auth.providers.Kakao
+import io.github.jan.supabase.auth.providers.Keycloak
+import io.github.jan.supabase.auth.providers.LinkedIn
+import io.github.jan.supabase.auth.providers.Notion
+import io.github.jan.supabase.auth.providers.OAuthProvider
+import io.github.jan.supabase.auth.providers.Slack
+import io.github.jan.supabase.auth.providers.Spotify
+import io.github.jan.supabase.auth.providers.Twitch
+import io.github.jan.supabase.auth.providers.Twitter
+import io.github.jan.supabase.auth.providers.WorkOS
@SupabaseInternal
expect fun svgPainter(bytes: ByteArray, density: Density): Painter
@SupabaseInternal
@Composable
+@Suppress("CyclomaticComplexMethod")
fun providerPainter(provider: OAuthProvider, density: Density): Painter? {
val painter by produceState(null) {
val data = when(provider) {
diff --git a/plugins/ImageLoaderIntegration/README.md b/plugins/ImageLoaderIntegration/README.md
index 637ca3c32..57fb831e6 100644
--- a/plugins/ImageLoaderIntegration/README.md
+++ b/plugins/ImageLoaderIntegration/README.md
@@ -4,27 +4,19 @@ Extends supabase-kt with a [Compose-ImageLoader](https://github.com/qdsfdhvh/com
Supported targets:
-| Target | **JVM** | **Android** | **JS** | **iOS** | **tvOS** | **watchOS** | **macOS** | **Windows** | **Linux** |
-|--------|---------|-------------|--------|---------|----------|-------------|-----------|-------------|-----------|
-| Status | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
+| Target | **JVM** | **Android** | **JS** | **iOS** |
+|--------|---------|-------------|--------|---------|
+| Status | ✅ | ✅ | ✅ | ✅ |
+
+> Wasm-JS is currently not supported due to a bug in the Compose-ImageLoader library.
In-depth Kotlin targets
-**iOS:** iosArm64, iosSimulatorArm64, iosX64
-
-**JS**: Browser, NodeJS
-
-**tvOS**: tvosArm64, tvosX64, tvosSimulatorArm64
-
-**watchOS**: watchosArm64, watchosX64, watchosSimulatorArm64
-
-**MacOS**: macosX64, macosArm64
-
-**Windows**: mingwX64
+**JS**: Browser
-**Linux**: linuxX64
+**iOS**: iosArm64, iosSimulatorArm64, iosX64
diff --git a/plugins/ImageLoaderIntegration/build.gradle.kts b/plugins/ImageLoaderIntegration/build.gradle.kts
index 7871b199f..94af58310 100644
--- a/plugins/ImageLoaderIntegration/build.gradle.kts
+++ b/plugins/ImageLoaderIntegration/build.gradle.kts
@@ -13,7 +13,10 @@ repositories {
@OptIn(org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi::class)
kotlin {
defaultConfig()
- composeTargets()
+ // composeTargets()
+ jsTarget()
+ jvmTargets()
+ iosTargets()
sourceSets {
val commonMain by getting {
dependencies {
diff --git a/sample/chat-demo-mpp/README.md b/sample/chat-demo-mpp/README.md
index c870b4b56..1b3c82632 100644
--- a/sample/chat-demo-mpp/README.md
+++ b/sample/chat-demo-mpp/README.md
@@ -4,7 +4,9 @@ This is a demo of a chat app using Compose Multiplatform, Koin and supabase-kt.
**Available platforms:** Android, iOS, Desktop, JS Canvas
-**Modules used:** Realtime, GoTrue, Postgrest, Compose Auth UI
+**Modules used:** Realtime, Auth*, Postgrest, Compose Auth UI
+
+* Integrated flows: Password, Google login & password recovery
https://user-images.githubusercontent.com/26686035/216710629-d809ff58-cd3b-449f-877f-4c6c773daec4.mp4
diff --git a/sample/chat-demo-mpp/android/src/main/java/io/github/jan/supabase/android/MainActivity.kt b/sample/chat-demo-mpp/android/src/main/java/io/github/jan/supabase/android/MainActivity.kt
index 99e15d065..746c205de 100644
--- a/sample/chat-demo-mpp/android/src/main/java/io/github/jan/supabase/android/MainActivity.kt
+++ b/sample/chat-demo-mpp/android/src/main/java/io/github/jan/supabase/android/MainActivity.kt
@@ -4,9 +4,9 @@ import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.material3.MaterialTheme
+import io.github.jan.supabase.auth.handleDeeplinks
import io.github.jan.supabase.common.App
import io.github.jan.supabase.common.ChatViewModel
-import io.github.jan.supabase.gotrue.handleDeeplinks
import org.koin.android.ext.android.inject
class MainActivity : ComponentActivity() {
diff --git a/sample/chat-demo-mpp/common/build.gradle.kts b/sample/chat-demo-mpp/common/build.gradle.kts
index 8608f1d2d..b4c4e25e2 100644
--- a/sample/chat-demo-mpp/common/build.gradle.kts
+++ b/sample/chat-demo-mpp/common/build.gradle.kts
@@ -1,5 +1,8 @@
@file:OptIn(org.jetbrains.compose.ExperimentalComposeLibrary::class)
+import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
+
+
plugins {
id(libs.plugins.kotlin.multiplatform.get().pluginId)
id(libs.plugins.compose.plugin.get().pluginId)
@@ -12,6 +15,7 @@ group = "io.github.jan.supabase"
version = "1.0-SNAPSHOT"
kotlin {
+ @OptIn(ExperimentalKotlinGradlePluginApi::class)
applyDefaultHierarchyTemplate {
common {
group("jvmAndAndroid") {
@@ -42,7 +46,7 @@ kotlin {
api(compose.foundation)
api(compose.material3)
api(compose.materialIconsExtended)
- addModules(SupabaseModule.GOTRUE, SupabaseModule.POSTGREST, SupabaseModule.REALTIME, SupabaseModule.COMPOSE_AUTH, SupabaseModule.COMPOSE_AUTH_UI)
+ addModules(SupabaseModule.AUTH, SupabaseModule.POSTGREST, SupabaseModule.REALTIME, SupabaseModule.COMPOSE_AUTH, SupabaseModule.COMPOSE_AUTH_UI)
api(libs.koin.core)
}
}
diff --git a/sample/chat-demo-mpp/common/src/androidMain/kotlin/io/github/jan/supabase/common/di/platformGoTrueConfig.kt b/sample/chat-demo-mpp/common/src/androidMain/kotlin/io/github/jan/supabase/common/di/platformGoTrueConfig.kt
index 119060501..6deb296ea 100644
--- a/sample/chat-demo-mpp/common/src/androidMain/kotlin/io/github/jan/supabase/common/di/platformGoTrueConfig.kt
+++ b/sample/chat-demo-mpp/common/src/androidMain/kotlin/io/github/jan/supabase/common/di/platformGoTrueConfig.kt
@@ -1,8 +1,10 @@
package io.github.jan.supabase.common.di
-import io.github.jan.supabase.gotrue.AuthConfig
+import io.github.jan.supabase.auth.AuthConfig
+import io.github.jan.supabase.auth.ExternalAuthAction
actual fun AuthConfig.platformGoTrueConfig() {
scheme = "io.jan.supabase"
host = "login"
+ defaultExternalAuthAction = ExternalAuthAction.CustomTabs()
}
\ No newline at end of file
diff --git a/sample/chat-demo-mpp/common/src/commonMain/kotlin/io/github/jan/supabase/common/App.kt b/sample/chat-demo-mpp/common/src/commonMain/kotlin/io/github/jan/supabase/common/App.kt
index 093bb5fef..9e8568586 100644
--- a/sample/chat-demo-mpp/common/src/commonMain/kotlin/io/github/jan/supabase/common/App.kt
+++ b/sample/chat-demo-mpp/common/src/commonMain/kotlin/io/github/jan/supabase/common/App.kt
@@ -11,9 +11,9 @@ import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
+import io.github.jan.supabase.auth.SessionStatus
import io.github.jan.supabase.common.ui.screen.ChatScreen
import io.github.jan.supabase.common.ui.screen.LoginScreen
-import io.github.jan.supabase.gotrue.SessionStatus
@Composable
fun App(viewModel: ChatViewModel) {
diff --git a/sample/chat-demo-mpp/common/src/commonMain/kotlin/io/github/jan/supabase/common/ChatViewModel.kt b/sample/chat-demo-mpp/common/src/commonMain/kotlin/io/github/jan/supabase/common/ChatViewModel.kt
index 6e0100c8c..0aee98f1e 100644
--- a/sample/chat-demo-mpp/common/src/commonMain/kotlin/io/github/jan/supabase/common/ChatViewModel.kt
+++ b/sample/chat-demo-mpp/common/src/commonMain/kotlin/io/github/jan/supabase/common/ChatViewModel.kt
@@ -3,23 +3,18 @@ package io.github.jan.supabase.common
import co.touchlab.kermit.Logger
import io.github.jan.supabase.SupabaseClient
+import io.github.jan.supabase.auth.SessionStatus
+import io.github.jan.supabase.common.net.AuthApi
import io.github.jan.supabase.common.net.Message
import io.github.jan.supabase.common.net.MessageApi
-import io.github.jan.supabase.gotrue.auth
-import io.github.jan.supabase.gotrue.providers.Google
-import io.github.jan.supabase.gotrue.providers.builtin.Email
-import io.github.jan.supabase.realtime.PostgresAction
-import io.github.jan.supabase.realtime.RealtimeChannel
-import io.github.jan.supabase.realtime.decodeRecord
-import io.github.jan.supabase.realtime.postgresChangeFlow
import io.github.jan.supabase.realtime.realtime
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
-import kotlinx.serialization.json.int
-import kotlinx.serialization.json.jsonPrimitive
expect open class MPViewModel() {
@@ -28,27 +23,25 @@ expect open class MPViewModel() {
}
class ChatViewModel(
val supabaseClient: SupabaseClient,
- private val realtimeChannel: RealtimeChannel,
- private val messageApi: MessageApi
+ private val messageApi: MessageApi,
+ private val authApi: AuthApi
) : MPViewModel() {
- val sessionStatus = supabaseClient.auth.sessionStatus
- val loginAlert = MutableStateFlow(null)
+ val sessionStatus = authApi.sessionStatus().stateIn(coroutineScope, SharingStarted.Eagerly, SessionStatus.NotAuthenticated(false))
+ val alert = MutableStateFlow(null)
val messages = MutableStateFlow>(emptyList())
+ val passwordReset = MutableStateFlow(false)
//Auth
fun signUp(email: String, password: String) {
coroutineScope.launch {
kotlin.runCatching {
- supabaseClient.auth.signUpWith(Email) {
- this.email = email
- this.password = password
- }
+ authApi.signUp(email, password)
}.onSuccess {
- loginAlert.value = "Successfully registered! Check your E-Mail to verify your account."
+ alert.value = "Successfully registered! Check your E-Mail to verify your account."
}.onFailure {
- loginAlert.value = "There was an error while registering: ${it.message}"
+ alert.value = "There was an error while registering: ${it.message}"
}
}
}
@@ -56,13 +49,10 @@ class ChatViewModel(
fun login(email: String, password: String) {
coroutineScope.launch {
kotlin.runCatching {
- supabaseClient.auth.signInWith(Email) {
- this.email = email
- this.password = password
- }
+ authApi.signIn(email, password)
}.onFailure {
it.printStackTrace()
- loginAlert.value = "There was an error while logging in. Check your credentials and try again."
+ alert.value = "There was an error while logging in. Check your credentials and try again."
}
}
}
@@ -70,47 +60,63 @@ class ChatViewModel(
fun loginWithGoogle() {
coroutineScope.launch {
kotlin.runCatching {
- supabaseClient.auth.signInWith(Google)
+ authApi.signInWithGoogle()
}
}
}
- fun logout() {
+ fun loginWithOTP(email: String, code: String, reset: Boolean) {
coroutineScope.launch {
kotlin.runCatching {
- supabaseClient.auth.signOut()
- messages.value = emptyList()
+ authApi.verifyOtp(email, code)
+ }.onSuccess {
+ passwordReset.value = reset
+ }.onFailure {
+ alert.value = "There was an error while verifying the OTP: ${it.message}"
}
}
}
- //Realtime
- fun connectToRealtime() {
+ fun resetPassword(email: String) {
coroutineScope.launch {
kotlin.runCatching {
- realtimeChannel.postgresChangeFlow("public") {
- table = "messages"
- }.onEach {
- when(it) {
- is PostgresAction.Delete -> messages.value = messages.value.filter { message -> message.id != it.oldRecord["id"]!!.jsonPrimitive.int }
- is PostgresAction.Insert -> messages.value = messages.value + it.decodeRecord()
- is PostgresAction.Select -> error("Select should not be possible")
- is PostgresAction.Update -> error("Update should not be possible")
- }
- }.launchIn(coroutineScope)
-
- realtimeChannel.subscribe()
+ authApi.resetPassword(email)
+ }
+ }
+ }
+ fun changePassword(password: String) {
+ coroutineScope.launch {
+ kotlin.runCatching {
+ authApi.changePassword(password)
+ }.onSuccess {
+ alert.value = "Password changed successfully!"
}.onFailure {
- it.printStackTrace()
+ alert.value = "There was an error while changing the password: ${it.message}"
}
}
}
- fun disconnectFromRealtime() {
+ fun logout() {
coroutineScope.launch {
kotlin.runCatching {
- supabaseClient.realtime.disconnect()
+ authApi.signOut()
+ messages.value = emptyList()
+ }
+ }
+ }
+
+ //Realtime
+ fun retrieveMessages() {
+ coroutineScope.launch {
+ kotlin.runCatching {
+ messageApi.retrieveMessages()
+ .onEach {
+ messages.value = it
+ }
+ .launchIn(coroutineScope)
+ }.onFailure {
+ Logger.e(it) { "Error while retrieving messages" }
}
}
}
@@ -136,14 +142,10 @@ class ChatViewModel(
}
}
- fun retrieveMessages() {
+ fun disconnectFromRealtime() {
coroutineScope.launch {
kotlin.runCatching {
- messageApi.retrieveMessages()
- }.onSuccess {
- messages.value = it
- }.onFailure {
- Logger.e(it) { "Error while retrieving messages" }
+ supabaseClient.realtime.removeAllChannels()
}
}
}
diff --git a/sample/chat-demo-mpp/common/src/commonMain/kotlin/io/github/jan/supabase/common/di/netModule.kt b/sample/chat-demo-mpp/common/src/commonMain/kotlin/io/github/jan/supabase/common/di/netModule.kt
index 9e109bdc1..b698a6c60 100644
--- a/sample/chat-demo-mpp/common/src/commonMain/kotlin/io/github/jan/supabase/common/di/netModule.kt
+++ b/sample/chat-demo-mpp/common/src/commonMain/kotlin/io/github/jan/supabase/common/di/netModule.kt
@@ -1,9 +1,12 @@
package io.github.jan.supabase.common.di
+import io.github.jan.supabase.common.net.AuthApi
+import io.github.jan.supabase.common.net.AuthApiImpl
import io.github.jan.supabase.common.net.MessageApi
import io.github.jan.supabase.common.net.MessageApiImpl
import org.koin.dsl.module
val netModule = module {
single { MessageApiImpl(get()) }
+ single { AuthApiImpl(get()) }
}
\ No newline at end of file
diff --git a/sample/chat-demo-mpp/common/src/commonMain/kotlin/io/github/jan/supabase/common/di/supabaseModule.kt b/sample/chat-demo-mpp/common/src/commonMain/kotlin/io/github/jan/supabase/common/di/supabaseModule.kt
index 75a1cc94d..42060f8e8 100644
--- a/sample/chat-demo-mpp/common/src/commonMain/kotlin/io/github/jan/supabase/common/di/supabaseModule.kt
+++ b/sample/chat-demo-mpp/common/src/commonMain/kotlin/io/github/jan/supabase/common/di/supabaseModule.kt
@@ -1,12 +1,12 @@
package io.github.jan.supabase.common.di
-import io.github.jan.supabase.SupabaseClient
+import io.github.jan.supabase.auth.Auth
+import io.github.jan.supabase.auth.AuthConfig
+import io.github.jan.supabase.auth.FlowType
import io.github.jan.supabase.createSupabaseClient
-import io.github.jan.supabase.gotrue.Auth
-import io.github.jan.supabase.gotrue.AuthConfig
+import io.github.jan.supabase.logging.LogLevel
import io.github.jan.supabase.postgrest.Postgrest
import io.github.jan.supabase.realtime.Realtime
-import io.github.jan.supabase.realtime.channel
import org.koin.dsl.module
expect fun AuthConfig.platformGoTrueConfig()
@@ -17,14 +17,13 @@ val supabaseModule = module {
supabaseUrl = "YOUR_URL",
supabaseKey = "YOUR_KEY"
) {
+ defaultLogLevel = LogLevel.DEBUG
install(Postgrest)
install(Auth) {
platformGoTrueConfig()
+ flowType = FlowType.PKCE
}
install(Realtime)
}
}
- single {
- get().channel("messages")
- }
}
\ No newline at end of file
diff --git a/sample/chat-demo-mpp/common/src/commonMain/kotlin/io/github/jan/supabase/common/net/AuthApi.kt b/sample/chat-demo-mpp/common/src/commonMain/kotlin/io/github/jan/supabase/common/net/AuthApi.kt
new file mode 100644
index 000000000..e6c6315fe
--- /dev/null
+++ b/sample/chat-demo-mpp/common/src/commonMain/kotlin/io/github/jan/supabase/common/net/AuthApi.kt
@@ -0,0 +1,78 @@
+package io.github.jan.supabase.common.net
+
+import io.github.jan.supabase.SupabaseClient
+import io.github.jan.supabase.auth.OtpType
+import io.github.jan.supabase.auth.SessionStatus
+import io.github.jan.supabase.auth.auth
+import io.github.jan.supabase.auth.providers.Google
+import io.github.jan.supabase.auth.providers.builtin.Email
+import kotlinx.coroutines.flow.Flow
+
+sealed interface AuthApi {
+
+ suspend fun signIn(email: String, password: String)
+
+ suspend fun signUp(email: String, password: String)
+
+ suspend fun signInWithGoogle()
+
+ suspend fun verifyOtp(email: String, otp: String)
+
+ suspend fun signOut()
+
+ suspend fun resetPassword(email: String)
+
+ suspend fun changePassword(newPassword: String)
+
+ fun sessionStatus(): Flow
+
+}
+
+internal class AuthApiImpl(
+ private val client: SupabaseClient
+) : AuthApi {
+
+ private val auth = client.auth
+
+ override fun sessionStatus(): Flow {
+ return auth.sessionStatus
+ }
+
+ override suspend fun verifyOtp(email: String, otp: String) {
+ auth.verifyEmailOtp(OtpType.Email.EMAIL, email, otp)
+ }
+
+ override suspend fun signInWithGoogle() {
+ auth.signInWith(Google)
+ }
+
+ override suspend fun signIn(email: String, password: String) {
+ auth.signInWith(Email) {
+ this.email = email
+ this.password = password
+ }
+ }
+
+ override suspend fun signUp(email: String, password: String) {
+ auth.signUpWith(Email) {
+ this.email = email
+ this.password = password
+ }
+ }
+
+ override suspend fun changePassword(newPassword: String) {
+ auth.updateUser {
+ this.password = newPassword
+ }
+ }
+
+ override suspend fun signOut() {
+ auth.signOut()
+ }
+
+ override suspend fun resetPassword(email: String) {
+ auth.resetPasswordForEmail(email)
+ }
+
+
+}
\ No newline at end of file
diff --git a/sample/chat-demo-mpp/common/src/commonMain/kotlin/io/github/jan/supabase/common/net/MessageApi.kt b/sample/chat-demo-mpp/common/src/commonMain/kotlin/io/github/jan/supabase/common/net/MessageApi.kt
index 6161333c2..a45d7be41 100644
--- a/sample/chat-demo-mpp/common/src/commonMain/kotlin/io/github/jan/supabase/common/net/MessageApi.kt
+++ b/sample/chat-demo-mpp/common/src/commonMain/kotlin/io/github/jan/supabase/common/net/MessageApi.kt
@@ -1,8 +1,11 @@
package io.github.jan.supabase.common.net
import io.github.jan.supabase.SupabaseClient
-import io.github.jan.supabase.gotrue.auth
+import io.github.jan.supabase.annotations.SupabaseExperimental
+import io.github.jan.supabase.auth.auth
import io.github.jan.supabase.postgrest.postgrest
+import io.github.jan.supabase.realtime.selectAsFlow
+import kotlinx.coroutines.flow.Flow
import kotlinx.datetime.Instant
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@@ -21,7 +24,7 @@ data class Message(
sealed interface MessageApi {
- suspend fun retrieveMessages(): List
+ suspend fun retrieveMessages(): Flow>
suspend fun createMessage(content: String): Message
@@ -35,7 +38,8 @@ internal class MessageApiImpl(
private val table = client.postgrest["messages"]
- override suspend fun retrieveMessages(): List = table.select().decodeList()
+ @OptIn(SupabaseExperimental::class)
+ override suspend fun retrieveMessages(): Flow> = table.selectAsFlow(Message::id)
override suspend fun createMessage(content: String): Message {
val user = (client.auth.currentSessionOrNull() ?: error("No session available")).user ?: error("No user available")
diff --git a/sample/chat-demo-mpp/common/src/commonMain/kotlin/io/github/jan/supabase/common/ui/components/MessageCard.kt b/sample/chat-demo-mpp/common/src/commonMain/kotlin/io/github/jan/supabase/common/ui/components/MessageCard.kt
index f5297f713..6a13cff02 100644
--- a/sample/chat-demo-mpp/common/src/commonMain/kotlin/io/github/jan/supabase/common/ui/components/MessageCard.kt
+++ b/sample/chat-demo-mpp/common/src/commonMain/kotlin/io/github/jan/supabase/common/ui/components/MessageCard.kt
@@ -28,7 +28,7 @@ fun MessageCard(message: Message, own: Boolean, modifier: Modifier, onDelete: ()
ElevatedCard(modifier = Modifier.widthIn(max = 200.dp), colors = CardDefaults.elevatedCardColors(containerColor = backgroundColor)) {
Column(modifier = Modifier.padding(12.dp)) {
Text(message.content)
- Text(message.creatorId, fontSize = 8.sp, modifier = Modifier.padding(top = 4.dp))
+ Text("UID: " + message.creatorId, fontSize = 8.sp, modifier = Modifier.padding(top = 4.dp))
}
}
if(own) {
diff --git a/sample/chat-demo-mpp/common/src/commonMain/kotlin/io/github/jan/supabase/common/ui/components/OTPDialog.kt b/sample/chat-demo-mpp/common/src/commonMain/kotlin/io/github/jan/supabase/common/ui/components/OTPDialog.kt
new file mode 100644
index 000000000..0ab4883be
--- /dev/null
+++ b/sample/chat-demo-mpp/common/src/commonMain/kotlin/io/github/jan/supabase/common/ui/components/OTPDialog.kt
@@ -0,0 +1,60 @@
+package io.github.jan.supabase.common.ui.components
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.material3.AlertDialog
+import androidx.compose.material3.OutlinedTextField
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextButton
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.text.input.KeyboardType
+
+sealed interface OTPDialogState {
+ data object Invisible : OTPDialogState
+ data class Visible(val title: String = "Sign in using an OTP", val resetFlow: Boolean = false, val email: String? = null) : OTPDialogState
+}
+
+@Composable
+fun OTPDialog(
+ email: String? = null,
+ title: String,
+ onDismiss: () -> Unit,
+ onConfirm: (email: String, code: String) -> Unit
+) {
+ var code by remember { mutableStateOf("") }
+ var otpEmail by remember { mutableStateOf("") }
+ AlertDialog(
+ onDismissRequest = onDismiss,
+ title = { Text(title) },
+ text = {
+ Column {
+ if(email == null) {
+ OutlinedTextField(otpEmail, { otpEmail = it }, label = { Text("Email") }, singleLine = true, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number))
+ } else {
+ Text("Please enter the code sent to $email.")
+ }
+ OutlinedTextField(code, { code = it }, label = { Text("Code") }, singleLine = true, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number))
+ }
+ },
+ confirmButton = {
+ TextButton(
+ onClick = {
+ onConfirm(email ?: otpEmail, code)
+ onDismiss()
+ },
+ enabled = (email ?: otpEmail).isNotBlank() && code.isNotBlank()
+ ) {
+ Text("Confirm")
+ }
+ },
+ dismissButton = {
+ TextButton(onClick = onDismiss) {
+ Text("Dismiss")
+ }
+ }
+ )
+}
diff --git a/sample/chat-demo-mpp/common/src/commonMain/kotlin/io/github/jan/supabase/common/ui/components/PasswordChangeDialog.kt b/sample/chat-demo-mpp/common/src/commonMain/kotlin/io/github/jan/supabase/common/ui/components/PasswordChangeDialog.kt
new file mode 100644
index 000000000..638fb015c
--- /dev/null
+++ b/sample/chat-demo-mpp/common/src/commonMain/kotlin/io/github/jan/supabase/common/ui/components/PasswordChangeDialog.kt
@@ -0,0 +1,61 @@
+package io.github.jan.supabase.common.ui.components
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.height
+import androidx.compose.material3.AlertDialog
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextButton
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import io.github.jan.supabase.compose.auth.ui.AuthForm
+import io.github.jan.supabase.compose.auth.ui.LocalAuthState
+import io.github.jan.supabase.compose.auth.ui.annotations.AuthUiExperimental
+import io.github.jan.supabase.compose.auth.ui.password.OutlinedPasswordField
+
+@OptIn(AuthUiExperimental::class, ExperimentalMaterial3Api::class)
+@Composable
+fun PasswordChangeDialog(
+ onDismiss: () -> Unit,
+ onConfirm: (newPassword: String) -> Unit
+) {
+ var password by remember { mutableStateOf("") }
+ AuthForm {
+ AlertDialog(
+ onDismissRequest = onDismiss,
+ title = { Text("Password change") },
+ text = {
+ Column {
+ Text("Please enter your new password.")
+ Spacer(Modifier.height(8.dp))
+ OutlinedPasswordField(
+ value = password,
+ onValueChange = { password = it },
+ )
+ }
+ },
+ confirmButton = {
+ TextButton(
+ onClick = {
+ onConfirm(password)
+ onDismiss()
+ },
+ enabled = LocalAuthState.current.validForm
+ ) {
+ Text("Confirm")
+ }
+ },
+ dismissButton = {
+ TextButton(onClick = onDismiss) {
+ Text("Dismiss")
+ }
+ }
+ )
+ }
+}
\ No newline at end of file
diff --git a/sample/chat-demo-mpp/common/src/commonMain/kotlin/io/github/jan/supabase/common/ui/components/PasswordRecoverDialog.kt b/sample/chat-demo-mpp/common/src/commonMain/kotlin/io/github/jan/supabase/common/ui/components/PasswordRecoverDialog.kt
new file mode 100644
index 000000000..f2d561230
--- /dev/null
+++ b/sample/chat-demo-mpp/common/src/commonMain/kotlin/io/github/jan/supabase/common/ui/components/PasswordRecoverDialog.kt
@@ -0,0 +1,48 @@
+package io.github.jan.supabase.common.ui.components
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.material3.AlertDialog
+import androidx.compose.material3.OutlinedTextField
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextButton
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.text.input.KeyboardType
+
+@Composable
+fun PasswordRecoveryDialog(
+ onDismiss: () -> Unit,
+ onConfirm: (email: String) -> Unit
+) {
+ var email by remember { mutableStateOf("") }
+ AlertDialog(
+ onDismissRequest = onDismiss,
+ title = { Text("Password recovery") },
+ text = {
+ Column {
+ Text("Please enter your new password.")
+ OutlinedTextField(email, { email = it }, label = { Text("Email") }, singleLine = true, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number))
+ }
+ },
+ confirmButton = {
+ TextButton(
+ onClick = {
+ onConfirm(email)
+ onDismiss()
+ },
+ enabled = email.isNotBlank()
+ ) {
+ Text("Confirm")
+ }
+ },
+ dismissButton = {
+ TextButton(onClick = onDismiss) {
+ Text("Dismiss")
+ }
+ }
+ )
+}
diff --git a/sample/chat-demo-mpp/common/src/commonMain/kotlin/io/github/jan/supabase/common/ui/screen/ChatScreen.kt b/sample/chat-demo-mpp/common/src/commonMain/kotlin/io/github/jan/supabase/common/ui/screen/ChatScreen.kt
index 1ac492751..12d4274d8 100644
--- a/sample/chat-demo-mpp/common/src/commonMain/kotlin/io/github/jan/supabase/common/ui/screen/ChatScreen.kt
+++ b/sample/chat-demo-mpp/common/src/commonMain/kotlin/io/github/jan/supabase/common/ui/screen/ChatScreen.kt
@@ -9,13 +9,15 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Send
-import androidx.compose.material3.Button
-import androidx.compose.material3.Divider
+import androidx.compose.material.icons.automirrored.filled.Logout
+import androidx.compose.material.icons.automirrored.filled.Send
+import androidx.compose.material3.AlertDialog
import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Text
+import androidx.compose.material3.TextButton
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
@@ -29,9 +31,10 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import io.github.jan.supabase.CurrentPlatformTarget
import io.github.jan.supabase.PlatformTarget
+import io.github.jan.supabase.auth.user.UserInfo
import io.github.jan.supabase.common.ChatViewModel
import io.github.jan.supabase.common.ui.components.MessageCard
-import io.github.jan.supabase.gotrue.user.UserInfo
+import io.github.jan.supabase.common.ui.components.PasswordChangeDialog
import kotlinx.coroutines.flow.map
@OptIn(ExperimentalMaterial3Api::class)
@@ -40,11 +43,12 @@ fun ChatScreen(viewModel: ChatViewModel, user: UserInfo) {
val messages by viewModel.messages.map { it.reversed() }.collectAsState(emptyList())
var message by remember { mutableStateOf("") }
val ownId = user.id
+ val reset by viewModel.passwordReset.collectAsState()
+ val alert by viewModel.alert.collectAsState()
LaunchedEffect(Unit) {
if(CurrentPlatformTarget in listOf(PlatformTarget.JVM, PlatformTarget.JS, PlatformTarget.ANDROID)) {
viewModel.retrieveMessages()
- viewModel.connectToRealtime()
}
}
@@ -62,7 +66,7 @@ fun ChatScreen(viewModel: ChatViewModel, user: UserInfo) {
}
}
}
- Divider(thickness = 1.dp, modifier = Modifier.fillMaxWidth().padding(vertical = 8.dp))
+ HorizontalDivider(thickness = 1.dp, modifier = Modifier.fillMaxWidth().padding(vertical = 8.dp))
Row(verticalAlignment = Alignment.CenterVertically) {
TextField(
value = message,
@@ -73,18 +77,55 @@ fun ChatScreen(viewModel: ChatViewModel, user: UserInfo) {
viewModel.createMessage(message)
message = ""
}, enabled = message.isNotBlank()) {
- Icon(Icons.Filled.Send, "Send")
+ Icon(Icons.AutoMirrored.Filled.Send, "Send")
}
}
}
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.TopStart) {
- Button({
- viewModel.disconnectFromRealtime()
- viewModel.logout()
- }, enabled = true, modifier = Modifier.padding(5.dp)) {
- Text("Logout")
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ IconButton({
+ viewModel.disconnectFromRealtime()
+ viewModel.logout()
+ }, modifier = Modifier.padding(5.dp)) {
+ Icon(Icons.AutoMirrored.Filled.Logout, "Logout")
+ }
+ TextButton(
+ onClick = {
+ viewModel.passwordReset.value = true
+ }
+ ) {
+ Text("Reset password")
+ }
}
}
+ if(reset) {
+ PasswordChangeDialog(
+ onDismiss = { viewModel.passwordReset.value = false },
+ onConfirm = { viewModel.changePassword(it) }
+ )
+ }
+
+ if(alert != null) {
+ AlertDialog(
+ onDismissRequest = {
+ viewModel.alert.value = null
+ },
+ title = { Text("Info") },
+ text = { Text(alert!!) },
+ confirmButton = {
+ TextButton(
+ onClick = {
+ viewModel.alert.value = null
+ }
+ ) {
+ Text("Ok")
+ }
+ }
+ )
+ }
+
}
\ No newline at end of file
diff --git a/sample/chat-demo-mpp/common/src/commonMain/kotlin/io/github/jan/supabase/common/ui/screen/LoginScreen.kt b/sample/chat-demo-mpp/common/src/commonMain/kotlin/io/github/jan/supabase/common/ui/screen/LoginScreen.kt
index d4cc03004..7ec089f16 100644
--- a/sample/chat-demo-mpp/common/src/commonMain/kotlin/io/github/jan/supabase/common/ui/screen/LoginScreen.kt
+++ b/sample/chat-demo-mpp/common/src/commonMain/kotlin/io/github/jan/supabase/common/ui/screen/LoginScreen.kt
@@ -31,18 +31,23 @@ import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp
import io.github.jan.supabase.annotations.SupabaseExperimental
+import io.github.jan.supabase.auth.providers.Google
import io.github.jan.supabase.common.ChatViewModel
+import io.github.jan.supabase.common.ui.components.OTPDialog
+import io.github.jan.supabase.common.ui.components.OTPDialogState
import io.github.jan.supabase.common.ui.components.PasswordField
+import io.github.jan.supabase.common.ui.components.PasswordRecoveryDialog
import io.github.jan.supabase.compose.auth.ui.ProviderButtonContent
import io.github.jan.supabase.compose.auth.ui.annotations.AuthUiExperimental
-import io.github.jan.supabase.gotrue.providers.Google
@OptIn(ExperimentalMaterial3Api::class, SupabaseExperimental::class, AuthUiExperimental::class)
@Composable
fun LoginScreen(viewModel: ChatViewModel) {
var signUp by remember { mutableStateOf(false) }
- val loginAlert by viewModel.loginAlert.collectAsState()
+ val loginAlert by viewModel.alert.collectAsState()
var email by remember { mutableStateOf("") }
+ var otpDialogState by remember { mutableStateOf(OTPDialogState.Invisible) }
+ var showPasswordRecoveryDialog by remember { mutableStateOf(false) }
Column(
modifier = Modifier.fillMaxSize(),
@@ -90,6 +95,12 @@ fun LoginScreen(viewModel: ChatViewModel) {
ProviderButtonContent(Google, text = if (signUp) "Sign Up with Google" else "Login with Google")
}
+ TextButton(
+ onClick = { otpDialogState = OTPDialogState.Visible(email) }
+ ) {
+ Text("Login with an OTP")
+ }
+
}
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.BottomCenter) {
TextButton(onClick = { signUp = !signUp }) {
@@ -97,17 +108,45 @@ fun LoginScreen(viewModel: ChatViewModel) {
}
}
+ Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.BottomEnd) {
+ TextButton(onClick = { showPasswordRecoveryDialog = true }) {
+ Text("Forgot password?")
+ }
+ }
+
+ if(otpDialogState is OTPDialogState.Visible) {
+ val state = (otpDialogState as OTPDialogState.Visible)
+ OTPDialog(
+ email = state.email,
+ title = state.title,
+ onDismiss = { otpDialogState = OTPDialogState.Invisible },
+ onConfirm = { email, code ->
+ viewModel.loginWithOTP(email, code, state.resetFlow)
+ }
+ )
+ }
+
+ if(showPasswordRecoveryDialog) {
+ PasswordRecoveryDialog(
+ onDismiss = { showPasswordRecoveryDialog = false },
+ onConfirm = { email ->
+ viewModel.resetPassword(email)
+ otpDialogState = OTPDialogState.Visible(title = "Password recovery", email = email, resetFlow = true)
+ }
+ )
+ }
+
if(loginAlert != null) {
AlertDialog(
onDismissRequest = {
- viewModel.loginAlert.value = null
+ viewModel.alert.value = null
},
text = {
Text(loginAlert!!)
},
confirmButton = {
TextButton(onClick = {
- viewModel.loginAlert.value = null
+ viewModel.alert.value = null
}) {
Text("Ok")
}
diff --git a/sample/chat-demo-mpp/common/src/desktopMain/kotlin/io/github/jan/supabase/common/di/platformGoTrueConfig.kt b/sample/chat-demo-mpp/common/src/desktopMain/kotlin/io/github/jan/supabase/common/di/platformGoTrueConfig.kt
index c2c86e17e..9db4758c4 100644
--- a/sample/chat-demo-mpp/common/src/desktopMain/kotlin/io/github/jan/supabase/common/di/platformGoTrueConfig.kt
+++ b/sample/chat-demo-mpp/common/src/desktopMain/kotlin/io/github/jan/supabase/common/di/platformGoTrueConfig.kt
@@ -1,6 +1,6 @@
package io.github.jan.supabase.common.di
-import io.github.jan.supabase.gotrue.AuthConfig
+import io.github.jan.supabase.auth.AuthConfig
actual fun AuthConfig.platformGoTrueConfig() {
httpCallbackConfig {
diff --git a/sample/chat-demo-mpp/common/src/iosMain/kotlin/io/github/jan/supabase/common/di/platformGoTrueConfig.kt b/sample/chat-demo-mpp/common/src/iosMain/kotlin/io/github/jan/supabase/common/di/platformGoTrueConfig.kt
index 119060501..efac2ce79 100644
--- a/sample/chat-demo-mpp/common/src/iosMain/kotlin/io/github/jan/supabase/common/di/platformGoTrueConfig.kt
+++ b/sample/chat-demo-mpp/common/src/iosMain/kotlin/io/github/jan/supabase/common/di/platformGoTrueConfig.kt
@@ -1,6 +1,6 @@
package io.github.jan.supabase.common.di
-import io.github.jan.supabase.gotrue.AuthConfig
+import io.github.jan.supabase.auth.AuthConfig
actual fun AuthConfig.platformGoTrueConfig() {
scheme = "io.jan.supabase"
diff --git a/sample/chat-demo-mpp/common/src/jsMain/kotlin/io/github/jan/supabase/common/di/platformGoTrueConfig.kt b/sample/chat-demo-mpp/common/src/jsMain/kotlin/io/github/jan/supabase/common/di/platformGoTrueConfig.kt
index 7331d7f76..494af766f 100644
--- a/sample/chat-demo-mpp/common/src/jsMain/kotlin/io/github/jan/supabase/common/di/platformGoTrueConfig.kt
+++ b/sample/chat-demo-mpp/common/src/jsMain/kotlin/io/github/jan/supabase/common/di/platformGoTrueConfig.kt
@@ -1,5 +1,5 @@
package io.github.jan.supabase.common.di
-import io.github.jan.supabase.gotrue.AuthConfig
+import io.github.jan.supabase.auth.AuthConfig
actual fun AuthConfig.platformGoTrueConfig() = Unit
\ No newline at end of file
diff --git a/sample/file-upload/android/src/main/java/io/github/jan/supabase/android/MainActivity.kt b/sample/file-upload/android/src/main/java/io/github/jan/supabase/android/MainActivity.kt
index 83fbd6d08..03b11f632 100644
--- a/sample/file-upload/android/src/main/java/io/github/jan/supabase/android/MainActivity.kt
+++ b/sample/file-upload/android/src/main/java/io/github/jan/supabase/android/MainActivity.kt
@@ -8,8 +8,6 @@ import androidx.compose.runtime.SideEffect
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.PermissionStatus
import com.google.accompanist.permissions.rememberPermissionState
-
-import co.touchlab.kermit.Logger
import io.github.jan.supabase.common.App
import io.github.jan.supabase.common.UploadViewModel
import org.koin.android.ext.android.inject
diff --git a/sample/file-upload/common/src/androidMain/kotlin/io/github/jan/supabase/common/Uploads.android.kt b/sample/file-upload/common/src/androidMain/kotlin/io/github/jan/supabase/common/Uploads.android.kt
index 57ac73cca..df8f65fe4 100644
--- a/sample/file-upload/common/src/androidMain/kotlin/io/github/jan/supabase/common/Uploads.android.kt
+++ b/sample/file-upload/common/src/androidMain/kotlin/io/github/jan/supabase/common/Uploads.android.kt
@@ -6,6 +6,7 @@ import android.provider.OpenableColumns
import io.github.vinceglb.filekit.core.PlatformFile
import io.ktor.util.cio.toByteReadChannel
import io.ktor.utils.io.ByteReadChannel
+import io.ktor.utils.io.discard
actual val PlatformFile.dataProducer: suspend (offset: Long) -> ByteReadChannel
get() {
diff --git a/sample/file-upload/common/src/commonMain/kotlin/io/github/jan/supabase/common/Utils.kt b/sample/file-upload/common/src/commonMain/kotlin/io/github/jan/supabase/common/Utils.kt
index 41436ede8..782ffcd24 100644
--- a/sample/file-upload/common/src/commonMain/kotlin/io/github/jan/supabase/common/Utils.kt
+++ b/sample/file-upload/common/src/commonMain/kotlin/io/github/jan/supabase/common/Utils.kt
@@ -4,7 +4,6 @@ import androidx.compose.ui.ExperimentalComposeUiApi
import io.github.jan.supabase.storage.resumable.ResumableClient
import io.github.jan.supabase.storage.resumable.ResumableUpload
import io.github.vinceglb.filekit.core.PlatformFile
-import io.ktor.utils.io.ByteReadChannel
import kotlinx.coroutines.Deferred
@OptIn(ExperimentalComposeUiApi::class)
@@ -12,13 +11,4 @@ expect fun parseFileTreeFromURIs(paths: List): List
expect fun parseFileTreeFromPath(path: String): List
-suspend fun ByteReadChannel.readAllBytes(size: Long): ByteArray {
- val buffer = ByteArray(size.toInt())
- var read = 0
- while(read < size) {
- read += readAvailable(buffer, read, size.toInt() - read)
- }
- return buffer
-}
-
expect suspend fun ResumableClient.continuePreviousPlatformUploads(): List>
\ No newline at end of file
diff --git a/sample/multi-factor-auth/android/src/main/java/io/github/jan/supabase/android/MainActivity.kt b/sample/multi-factor-auth/android/src/main/java/io/github/jan/supabase/android/MainActivity.kt
index 8fa4fecb3..66f6464a7 100644
--- a/sample/multi-factor-auth/android/src/main/java/io/github/jan/supabase/android/MainActivity.kt
+++ b/sample/multi-factor-auth/android/src/main/java/io/github/jan/supabase/android/MainActivity.kt
@@ -4,9 +4,9 @@ import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.material3.MaterialTheme
+import io.github.jan.supabase.auth.handleDeeplinks
import io.github.jan.supabase.common.App
import io.github.jan.supabase.common.AppViewModel
-import io.github.jan.supabase.gotrue.handleDeeplinks
import org.koin.android.ext.android.inject
class MainActivity : ComponentActivity() {
diff --git a/sample/multi-factor-auth/common/build.gradle.kts b/sample/multi-factor-auth/common/build.gradle.kts
index 481d32c42..148d73afc 100644
--- a/sample/multi-factor-auth/common/build.gradle.kts
+++ b/sample/multi-factor-auth/common/build.gradle.kts
@@ -35,7 +35,7 @@ kotlin {
api(compose.foundation)
api(compose.material3)
api(compose.materialIconsExtended)
- addModules(SupabaseModule.GOTRUE)
+ addModules(SupabaseModule.AUTH)
api(libs.koin.core)
}
}
@@ -51,13 +51,12 @@ kotlin {
api(libs.koin.android)
api(libs.androidx.lifecycle.viewmodel.ktx)
api(libs.androidx.lifecycle.viewmodel.compose)
- api(libs.coil.svg)
- api(libs.coil.compose)
- api(libs.coil)
+ api(libs.coil2.svg)
+ api(libs.coil2.compose)
+ api(libs.coil2)
}
}
val desktopMain by getting {
- dependsOn(nonJsMain)
dependencies {
api(compose.preview)
}
diff --git a/sample/multi-factor-auth/common/src/androidMain/kotlin/io/github/jan/supabase/common/di/platformGoTrueConfig.kt b/sample/multi-factor-auth/common/src/androidMain/kotlin/io/github/jan/supabase/common/di/platformGoTrueConfig.kt
index 119060501..efac2ce79 100644
--- a/sample/multi-factor-auth/common/src/androidMain/kotlin/io/github/jan/supabase/common/di/platformGoTrueConfig.kt
+++ b/sample/multi-factor-auth/common/src/androidMain/kotlin/io/github/jan/supabase/common/di/platformGoTrueConfig.kt
@@ -1,6 +1,6 @@
package io.github.jan.supabase.common.di
-import io.github.jan.supabase.gotrue.AuthConfig
+import io.github.jan.supabase.auth.AuthConfig
actual fun AuthConfig.platformGoTrueConfig() {
scheme = "io.jan.supabase"
diff --git a/sample/multi-factor-auth/common/src/androidMain/kotlin/io/github/jan/supabase/common/ui/components/QRCode.kt b/sample/multi-factor-auth/common/src/androidMain/kotlin/io/github/jan/supabase/common/ui/components/QRCode.kt
index 33e0db1f7..226be5461 100644
--- a/sample/multi-factor-auth/common/src/androidMain/kotlin/io/github/jan/supabase/common/ui/components/QRCode.kt
+++ b/sample/multi-factor-auth/common/src/androidMain/kotlin/io/github/jan/supabase/common/ui/components/QRCode.kt
@@ -3,7 +3,6 @@ package io.github.jan.supabase.common.ui.components
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalContext
import coil.ImageLoader
import coil.compose.AsyncImage
diff --git a/sample/multi-factor-auth/common/src/commonMain/kotlin/io/github/jan/supabase/common/App.kt b/sample/multi-factor-auth/common/src/commonMain/kotlin/io/github/jan/supabase/common/App.kt
index 3959a8792..1eac458f4 100644
--- a/sample/multi-factor-auth/common/src/commonMain/kotlin/io/github/jan/supabase/common/App.kt
+++ b/sample/multi-factor-auth/common/src/commonMain/kotlin/io/github/jan/supabase/common/App.kt
@@ -11,10 +11,10 @@ import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
+import io.github.jan.supabase.auth.SessionStatus
import io.github.jan.supabase.common.ui.components.AlertDialog
import io.github.jan.supabase.common.ui.screen.LoginScreen
import io.github.jan.supabase.common.ui.screen.MfaScreen
-import io.github.jan.supabase.gotrue.SessionStatus
@Composable
fun App(viewModel: AppViewModel) {
diff --git a/sample/multi-factor-auth/common/src/commonMain/kotlin/io/github/jan/supabase/common/AppViewModel.kt b/sample/multi-factor-auth/common/src/commonMain/kotlin/io/github/jan/supabase/common/AppViewModel.kt
index ec6222a62..f0e6f6b5f 100644
--- a/sample/multi-factor-auth/common/src/commonMain/kotlin/io/github/jan/supabase/common/AppViewModel.kt
+++ b/sample/multi-factor-auth/common/src/commonMain/kotlin/io/github/jan/supabase/common/AppViewModel.kt
@@ -2,12 +2,12 @@ package io.github.jan.supabase.common
import io.github.jan.supabase.SupabaseClient
+import io.github.jan.supabase.auth.auth
+import io.github.jan.supabase.auth.mfa.FactorType
+import io.github.jan.supabase.auth.mfa.MfaFactor
+import io.github.jan.supabase.auth.providers.Google
+import io.github.jan.supabase.auth.providers.builtin.Email
import io.github.jan.supabase.exceptions.RestException
-import io.github.jan.supabase.gotrue.auth
-import io.github.jan.supabase.gotrue.mfa.FactorType
-import io.github.jan.supabase.gotrue.mfa.MfaFactor
-import io.github.jan.supabase.gotrue.providers.Google
-import io.github.jan.supabase.gotrue.providers.builtin.Email
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
diff --git a/sample/multi-factor-auth/common/src/commonMain/kotlin/io/github/jan/supabase/common/di/supabaseModule.kt b/sample/multi-factor-auth/common/src/commonMain/kotlin/io/github/jan/supabase/common/di/supabaseModule.kt
index d63fa54d7..c5aadbe0c 100644
--- a/sample/multi-factor-auth/common/src/commonMain/kotlin/io/github/jan/supabase/common/di/supabaseModule.kt
+++ b/sample/multi-factor-auth/common/src/commonMain/kotlin/io/github/jan/supabase/common/di/supabaseModule.kt
@@ -1,8 +1,8 @@
package io.github.jan.supabase.common.di
+import io.github.jan.supabase.auth.Auth
+import io.github.jan.supabase.auth.AuthConfig
import io.github.jan.supabase.createSupabaseClient
-import io.github.jan.supabase.gotrue.Auth
-import io.github.jan.supabase.gotrue.AuthConfig
import org.koin.dsl.module
expect fun AuthConfig.platformGoTrueConfig()
diff --git a/sample/multi-factor-auth/common/src/commonMain/kotlin/io/github/jan/supabase/common/ui/screen/LoginScreen.kt b/sample/multi-factor-auth/common/src/commonMain/kotlin/io/github/jan/supabase/common/ui/screen/LoginScreen.kt
index 88ca1f152..ec2591321 100644
--- a/sample/multi-factor-auth/common/src/commonMain/kotlin/io/github/jan/supabase/common/ui/screen/LoginScreen.kt
+++ b/sample/multi-factor-auth/common/src/commonMain/kotlin/io/github/jan/supabase/common/ui/screen/LoginScreen.kt
@@ -16,7 +16,6 @@ import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -29,7 +28,6 @@ import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp
import io.github.jan.supabase.common.AppViewModel
-import io.github.jan.supabase.common.ui.components.AlertDialog
import io.github.jan.supabase.common.ui.components.GoogleButton
import io.github.jan.supabase.common.ui.components.PasswordField
diff --git a/sample/multi-factor-auth/common/src/commonMain/kotlin/io/github/jan/supabase/common/ui/screen/MfaScreen.kt b/sample/multi-factor-auth/common/src/commonMain/kotlin/io/github/jan/supabase/common/ui/screen/MfaScreen.kt
index bd53c2270..2a5d04ec8 100644
--- a/sample/multi-factor-auth/common/src/commonMain/kotlin/io/github/jan/supabase/common/ui/screen/MfaScreen.kt
+++ b/sample/multi-factor-auth/common/src/commonMain/kotlin/io/github/jan/supabase/common/ui/screen/MfaScreen.kt
@@ -3,8 +3,8 @@ package io.github.jan.supabase.common.ui.screen
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
+import io.github.jan.supabase.auth.mfa.MfaStatus
import io.github.jan.supabase.common.AppViewModel
-import io.github.jan.supabase.gotrue.mfa.MfaStatus
@Composable
fun MfaScreen(viewModel: AppViewModel) {
diff --git a/sample/multi-factor-auth/common/src/commonMain/kotlin/io/github/jan/supabase/common/ui/screen/MfaSetupScreen.kt b/sample/multi-factor-auth/common/src/commonMain/kotlin/io/github/jan/supabase/common/ui/screen/MfaSetupScreen.kt
index 76337956b..8ad5636fd 100644
--- a/sample/multi-factor-auth/common/src/commonMain/kotlin/io/github/jan/supabase/common/ui/screen/MfaSetupScreen.kt
+++ b/sample/multi-factor-auth/common/src/commonMain/kotlin/io/github/jan/supabase/common/ui/screen/MfaSetupScreen.kt
@@ -1,7 +1,6 @@
package io.github.jan.supabase.common.ui.screen
import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
@@ -15,7 +14,6 @@ import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
-import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
@@ -24,14 +22,13 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.scale
import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.unit.dp
+import io.github.jan.supabase.auth.mfa.FactorType
+import io.github.jan.supabase.auth.mfa.MfaFactor
import io.github.jan.supabase.common.AppViewModel
import io.github.jan.supabase.common.ui.components.QRCode
-import io.github.jan.supabase.gotrue.mfa.FactorType
-import io.github.jan.supabase.gotrue.mfa.MfaFactor
@OptIn(ExperimentalMaterial3Api::class)
@Composable
diff --git a/sample/multi-factor-auth/common/src/desktopMain/kotlin/io/github/jan/supabase/common/di/platformGoTrueConfig.kt b/sample/multi-factor-auth/common/src/desktopMain/kotlin/io/github/jan/supabase/common/di/platformGoTrueConfig.kt
index ca4d938cf..daa63e2b1 100644
--- a/sample/multi-factor-auth/common/src/desktopMain/kotlin/io/github/jan/supabase/common/di/platformGoTrueConfig.kt
+++ b/sample/multi-factor-auth/common/src/desktopMain/kotlin/io/github/jan/supabase/common/di/platformGoTrueConfig.kt
@@ -1,6 +1,6 @@
package io.github.jan.supabase.common.di
-import io.github.jan.supabase.gotrue.AuthConfig
+import io.github.jan.supabase.auth.AuthConfig
actual fun AuthConfig.platformGoTrueConfig() {
httpCallbackConfig { htmlTitle = "Chat App" }
diff --git a/sample/multi-factor-auth/common/src/desktopMain/kotlin/io/github/jan/supabase/common/ui/components/QRCode.kt b/sample/multi-factor-auth/common/src/desktopMain/kotlin/io/github/jan/supabase/common/ui/components/QRCode.kt
index 4fec6dc29..0d4514743 100644
--- a/sample/multi-factor-auth/common/src/desktopMain/kotlin/io/github/jan/supabase/common/ui/components/QRCode.kt
+++ b/sample/multi-factor-auth/common/src/desktopMain/kotlin/io/github/jan/supabase/common/ui/components/QRCode.kt
@@ -3,11 +3,9 @@ package io.github.jan.supabase.common.ui.components
import androidx.compose.foundation.Image
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
-import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.loadSvgPainter
import androidx.compose.ui.unit.Density
-import java.io.ByteArrayInputStream
@Composable
actual fun QRCode(svgData: String, modifier: Modifier) {
diff --git a/sample/multi-factor-auth/common/src/jsMain/kotlin/io/github/jan/supabase/common/di/platformGoTrueConfig.kt b/sample/multi-factor-auth/common/src/jsMain/kotlin/io/github/jan/supabase/common/di/platformGoTrueConfig.kt
index 5919bfd33..a0e2b25b3 100644
--- a/sample/multi-factor-auth/common/src/jsMain/kotlin/io/github/jan/supabase/common/di/platformGoTrueConfig.kt
+++ b/sample/multi-factor-auth/common/src/jsMain/kotlin/io/github/jan/supabase/common/di/platformGoTrueConfig.kt
@@ -1,6 +1,6 @@
package io.github.jan.supabase.common.di
-import io.github.jan.supabase.gotrue.AuthConfig
+import io.github.jan.supabase.auth.AuthConfig
actual fun AuthConfig.platformGoTrueConfig() {
}
\ No newline at end of file
diff --git a/sample/multi-factor-auth/common/src/jsMain/kotlin/io/github/jan/supabase/common/ui/components/QRCode.kt b/sample/multi-factor-auth/common/src/jsMain/kotlin/io/github/jan/supabase/common/ui/components/QRCode.kt
index 6105b8bac..c6c27b5c0 100644
--- a/sample/multi-factor-auth/common/src/jsMain/kotlin/io/github/jan/supabase/common/ui/components/QRCode.kt
+++ b/sample/multi-factor-auth/common/src/jsMain/kotlin/io/github/jan/supabase/common/ui/components/QRCode.kt
@@ -1,7 +1,6 @@
package io.github.jan.supabase.common.ui.components
import androidx.compose.foundation.Canvas
-import androidx.compose.foundation.layout.Spacer
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
diff --git a/serializers/Jackson/build.gradle.kts b/serializers/Jackson/build.gradle.kts
index 23c1e834b..1b5e1145c 100644
--- a/serializers/Jackson/build.gradle.kts
+++ b/serializers/Jackson/build.gradle.kts
@@ -12,10 +12,8 @@ repositories {
@OptIn(org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi::class)
kotlin {
defaultConfig()
- jvm()
- androidTarget {
- publishLibraryVariants("release", "debug")
- }
+ configuredAndroidTarget()
+ configuredJvmTarget()
sourceSets {
val commonMain by getting {
dependencies {
diff --git a/serializers/Moshi/build.gradle.kts b/serializers/Moshi/build.gradle.kts
index b0d89e2b4..ea836ba61 100644
--- a/serializers/Moshi/build.gradle.kts
+++ b/serializers/Moshi/build.gradle.kts
@@ -12,10 +12,8 @@ repositories {
@OptIn(org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi::class)
kotlin {
defaultConfig()
- jvm()
- androidTarget {
- publishLibraryVariants("release", "debug")
- }
+ configuredAndroidTarget()
+ configuredJvmTarget()
sourceSets {
val commonMain by getting {
dependencies {
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 1af111188..431bff73d 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -1,3 +1,4 @@
+
pluginManagement {
repositories {
google()
@@ -11,7 +12,7 @@ plugins {
}
// Main Modules
-include("GoTrue")
+include("Auth")
include("Postgrest")
include("Storage")
include("Realtime")
@@ -32,6 +33,7 @@ project(":serializers:Jackson").name = "serializer-jackson"
include(":plugins:ApolloGraphQL")
include(":plugins:ComposeAuth")
include(":plugins:ComposeAuthUI")
+include(":plugins:Coil3Integration")
include(":plugins:CoilIntegration")
include(":plugins:ImageLoaderIntegration")
@@ -43,7 +45,7 @@ if (System.getProperty("LibrariesOnly") != "true") {
}
// Renames
-project(":GoTrue").name = "gotrue-kt"
+project(":Auth").name = "auth-kt"
project(":Postgrest").name = "postgrest-kt"
project(":Storage").name = "storage-kt"
project(":Realtime").name = "realtime-kt"
@@ -52,6 +54,7 @@ project(":Supabase").name = "supabase-kt"
project(":plugins:ApolloGraphQL").name = "apollo-graphql"
project(":plugins:ComposeAuth").name = "compose-auth"
project(":plugins:ComposeAuthUI").name = "compose-auth-ui"
+project(":plugins:Coil3Integration").name = "coil3-integration"
project(":plugins:CoilIntegration").name = "coil-integration"
project(":plugins:ImageLoaderIntegration").name = "imageloader-integration"
rootProject.name = "supabase-kt"