├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ ├── feature_request.yml │ └── question.yml ├── dependabot.yml ├── images │ ├── desktop_supabase.png │ └── img.png ├── labeler.yml └── workflows │ ├── build.yml │ ├── cache.yml │ ├── detekt.yml │ ├── dokka.yml │ ├── labeler.yml │ ├── release.yml │ ├── samples.yml │ └── test.yml ├── .gitignore ├── Auth ├── README.md ├── build.gradle.kts └── src │ ├── androidMain │ ├── AndroidManifest.xml │ └── kotlin │ │ └── io │ │ └── github │ │ └── jan │ │ └── supabase │ │ └── auth │ │ ├── Android.kt │ │ ├── AuthConfig.kt │ │ ├── RedirectUrl.android.kt │ │ ├── Utils.android.kt │ │ ├── providers │ │ └── ExternalAuthConfig.kt │ │ └── setupPlatform.kt │ ├── androidUnitTest │ └── kotlin │ │ └── platformSettings.kt │ ├── appleMain │ └── kotlin │ │ └── io │ │ └── github │ │ └── jan │ │ └── supabase │ │ └── auth │ │ ├── Apple.kt │ │ ├── RedirectUrl.apple.kt │ │ ├── Utils.apple.kt │ │ ├── providers │ │ ├── ExternalAuthConfig.kt │ │ └── OAuthProvider.kt │ │ └── setupPlatform.kt │ ├── appleTest │ └── kotlin │ │ └── platformSettings.kt │ ├── commonMain │ └── kotlin │ │ └── io │ │ └── github │ │ └── jan │ │ └── supabase │ │ └── auth │ │ ├── AccessToken.kt │ │ ├── Auth.kt │ │ ├── AuthConfig.kt │ │ ├── AuthExtensions.kt │ │ ├── AuthImpl.kt │ │ ├── AuthenticatedSupabaseApi.kt │ │ ├── CodeVerifierCache.kt │ │ ├── GoTrueErrorResponse.kt │ │ ├── JsonUtils.kt │ │ ├── MinimalConfig.kt │ │ ├── OtpType.kt │ │ ├── PKCE.kt │ │ ├── PostgrestFilterDSL.kt │ │ ├── RedirectUrl.kt │ │ ├── SessionManager.kt │ │ ├── SignOutScope.kt │ │ ├── UrlLauncher.kt │ │ ├── UrlUtils.kt │ │ ├── Utils.kt │ │ ├── admin │ │ ├── AdminApi.kt │ │ ├── AdminUserBuilder.kt │ │ ├── AdminUserUpdateBuilder.kt │ │ └── LinkType.kt │ │ ├── event │ │ └── AuthEvent.kt │ │ ├── exception │ │ ├── AuthErrorCode.kt │ │ ├── AuthRestException.kt │ │ ├── AuthSessionMissingException.kt │ │ └── AuthWeakPasswordException.kt │ │ ├── mfa │ │ ├── AuthenticatorAssuranceLevel.kt │ │ ├── FactorType.kt │ │ ├── MfaApi.kt │ │ ├── MfaChallenge.kt │ │ ├── MfaFactor.kt │ │ └── MfaStatus.kt │ │ ├── providers │ │ ├── AuthProvider.kt │ │ ├── ExternalAuthConfig.kt │ │ ├── IDTokenProvider.kt │ │ ├── OAuthProvider.kt │ │ ├── Providers.kt │ │ └── builtin │ │ │ ├── CaptchaTokenSerializer.kt │ │ │ ├── DefaultAuthProvider.kt │ │ │ ├── Email.kt │ │ │ ├── IDToken.kt │ │ │ ├── OTP.kt │ │ │ ├── Phone.kt │ │ │ └── SSO.kt │ │ ├── status │ │ ├── RefreshFailureCause.kt │ │ ├── SessionSource.kt │ │ └── SessionStatus.kt │ │ └── user │ │ ├── Identity.kt │ │ ├── UserInfo.kt │ │ ├── UserSession.kt │ │ └── UserUpdateBuilder.kt │ ├── commonTest │ └── kotlin │ │ ├── AccessTokenTest.kt │ │ ├── AdminApiTest.kt │ │ ├── AuthApiTest.kt │ │ ├── AuthRestExceptionTest.kt │ │ ├── AuthTest.kt │ │ ├── AuthTestUtils.kt │ │ ├── CodeVerifierCacheTest.kt │ │ ├── MemorySessionManagerTest.kt │ │ ├── MfaApiTest.kt │ │ └── UrlUtilsTest.kt │ ├── desktopMain │ └── kotlin │ │ └── io │ │ └── github │ │ └── jan │ │ └── supabase │ │ └── auth │ │ ├── AuthConfig.kt │ │ ├── Utils.desktop.kt │ │ └── server │ │ ├── HttpCallbackHtml.kt │ │ ├── HttpCallbackRoutes.kt │ │ └── HttpCallbackServer.kt │ ├── iosMain │ └── kotlin │ │ └── io │ │ └── github │ │ └── jan │ │ └── supabase │ │ └── auth │ │ ├── AuthConfig.kt │ │ └── providers │ │ └── openUrl.kt │ ├── jsMain │ └── kotlin │ │ └── io │ │ └── github │ │ └── jan │ │ └── supabase │ │ └── auth │ │ ├── AuthConfig.kt │ │ ├── RedirectUrl.js.kt │ │ ├── Utils.js.kt │ │ ├── providers │ │ └── ExternalAuthConfig.kt │ │ └── setupPlatform.kt │ ├── jsTest │ └── kotlin │ │ └── platformSettings.kt │ ├── jvmMain │ └── kotlin │ │ └── io │ │ └── github │ │ └── jan │ │ └── supabase │ │ └── auth │ │ ├── RedirectUrl.jvm.kt │ │ ├── Utils.jvm.kt │ │ ├── providers │ │ └── ExternalAuthConfig.kt │ │ └── setupPlatform.kt │ ├── jvmTest │ └── kotlin │ │ └── platformSettings.kt │ ├── linuxMain │ └── kotlin │ │ └── io │ │ └── github │ │ └── jan │ │ └── supabase │ │ └── auth │ │ ├── SettingsUtil.kt │ │ ├── Utils.linux.kt │ │ ├── generateRedirectUrl.kt │ │ ├── providers │ │ └── ExternalAuthConfig.kt │ │ └── setupPlatform.kt │ ├── linuxTest │ └── kotlin │ │ └── platformSettings.kt │ ├── macosMain │ └── kotlin │ │ └── io │ │ └── github │ │ └── jan │ │ └── supabase │ │ └── auth │ │ └── providers │ │ └── openUrl.kt │ ├── mingwMain │ └── kotlin │ │ └── io │ │ └── github │ │ └── jan │ │ └── supabase │ │ └── auth │ │ ├── Utils.mingw.kt │ │ ├── generateRedirectUrl.kt │ │ ├── providers │ │ └── ExternalAuthConfig.kt │ │ └── setupPlatform.kt │ ├── mingwTest │ └── kotlin │ │ └── platformSettings.kt │ ├── nonDesktopMain │ └── kotlin │ │ └── io │ │ └── github │ │ └── jan │ │ └── supabase │ │ └── auth │ │ └── Utils.kt │ ├── settingsMain │ └── kotlin │ │ └── io │ │ └── github │ │ └── jan │ │ └── supabase │ │ └── auth │ │ ├── SettingsCodeVerifierCache.kt │ │ ├── SettingsSessionManager.kt │ │ └── SettingsUtil.kt │ ├── settingsTest │ └── kotlin │ │ ├── SettingsCodeVerifierCacheTest.kt │ │ ├── SettingsSessionManagerTest.kt │ │ └── SettingsTest.kt │ ├── tvosMain │ └── kotlin │ │ └── io │ │ └── github │ │ └── jan │ │ └── supabase │ │ └── auth │ │ ├── AuthConfig.kt │ │ └── providers │ │ └── openUrl.kt │ ├── wasmJsMain │ └── kotlin │ │ └── io │ │ └── github │ │ └── jan │ │ └── supabase │ │ └── auth │ │ ├── AuthConfig.kt │ │ ├── RedirectUrl.kt │ │ ├── Utils.wasmJs.kt │ │ ├── providers │ │ └── ExternalAuthConfig.kt │ │ └── setupPlatform.kt │ ├── wasmJsTest │ └── kotlin │ │ └── platformSettings.kt │ └── watchosMain │ └── kotlin │ └── io │ └── github │ └── jan │ └── supabase │ └── auth │ ├── AuthConfig.kt │ └── providers │ └── openUrl.kt ├── CHANGELOG.md ├── CODEOWNERS ├── CONTRIBUTING.md ├── Functions ├── README.md ├── build.gradle.kts └── src │ ├── androidMain │ └── AndroidManifest.xml │ ├── commonMain │ └── kotlin │ │ └── io │ │ └── github │ │ └── jan │ │ └── supabase │ │ └── functions │ │ ├── EdgeFunction.kt │ │ ├── FunctionRegion.kt │ │ └── Functions.kt │ └── commonTest │ └── kotlin │ ├── EdgeFunctionTest.kt │ └── FunctionsTest.kt ├── GoTrue └── README.md ├── LICENSE ├── MIGRATION.md ├── Postgrest ├── README.md ├── build.gradle.kts └── src │ ├── androidMain │ ├── AndroidManifest.xml │ └── kotlin │ │ └── io │ │ └── github │ │ └── jan │ │ └── supabase │ │ └── postgrest │ │ └── getColumnName.kt │ ├── appleMain │ └── kotlin │ │ └── io │ │ └── github │ │ └── jan │ │ └── supabase │ │ └── postgrest │ │ └── getColumnName.kt │ ├── commonMain │ └── kotlin │ │ └── io │ │ └── github │ │ └── jan │ │ └── supabase │ │ └── postgrest │ │ ├── Postgrest.kt │ │ ├── PostgrestDsl.kt │ │ ├── PostgrestErrorResponse.kt │ │ ├── PostgrestImpl.kt │ │ ├── PostgrestRpc.kt │ │ ├── PropertyConversionMethod.kt │ │ ├── Utils.kt │ │ ├── exception │ │ └── PostgrestRestException.kt │ │ ├── executor │ │ ├── PostgrestHttpExtension.kt │ │ ├── RequestExecutor.kt │ │ └── RestRequestExecutor.kt │ │ ├── query │ │ ├── Columns.kt │ │ ├── Order.kt │ │ ├── PostgrestQueryBuilder.kt │ │ ├── PostgrestRequestBuilder.kt │ │ ├── PostgrestUpdate.kt │ │ ├── filter │ │ │ ├── FilterOperation.kt │ │ │ ├── FilterOperator.kt │ │ │ ├── PostgrestFilterBuilder.kt │ │ │ └── TextSearchType.kt │ │ └── request │ │ │ ├── InsertRequestBuilder.kt │ │ │ ├── RpcRequestBuilder.kt │ │ │ ├── SelectRequestBuilder.kt │ │ │ └── UpsertRequestBuilder.kt │ │ ├── request │ │ ├── DeleteRequest.kt │ │ ├── InsertRequest.kt │ │ ├── PostgrestRequest.kt │ │ ├── RpcRequest.kt │ │ ├── SelectRequest.kt │ │ └── UpdateRequest.kt │ │ └── result │ │ └── PostgrestResult.kt │ ├── commonTest │ └── kotlin │ │ ├── ColumnsTest.kt │ │ ├── PostgrestFilterBuilderTest.kt │ │ ├── PostgrestRequestBuilderTest.kt │ │ ├── PostgrestTest.kt │ │ ├── Utils.kt │ │ └── request │ │ ├── DeleteRequestTest.kt │ │ ├── InsertRequestTest.kt │ │ ├── RpcRequestTest.kt │ │ ├── SelectRequestTest.kt │ │ └── UpdateRequestTest.kt │ ├── jsMain │ └── kotlin │ │ └── io │ │ └── github │ │ └── jan │ │ └── supabase │ │ └── postgrest │ │ └── getColumnName.kt │ ├── jvmMain │ └── kotlin │ │ └── io │ │ └── github │ │ └── jan │ │ └── supabase │ │ └── postgrest │ │ └── getColumnName.kt │ ├── linuxX64Main │ └── kotlin │ │ └── io │ │ └── github │ │ └── jan │ │ └── supabase │ │ └── postgrest │ │ └── getSerialName.kt │ ├── mingwX64Main │ └── kotlin │ │ └── io │ │ └── github │ │ └── jan │ │ └── supabase │ │ └── postgrest │ │ └── getSerialName.kt │ └── wasmJsMain │ └── kotlin │ └── io │ └── github │ └── jan │ └── supabase │ └── postgrest │ └── getColumnName.kt ├── README.md ├── Realtime ├── README.md ├── build.gradle.kts └── src │ ├── androidMain │ └── AndroidManifest.xml │ ├── commonMain │ └── kotlin │ │ └── io │ │ └── github │ │ └── jan │ │ └── supabase │ │ └── realtime │ │ ├── CallbackManager.kt │ │ ├── PostgresAction.kt │ │ ├── PostgresChangeFilter.kt │ │ ├── PostgrestExtensions.kt │ │ ├── PresenceAction.kt │ │ ├── Realtime.kt │ │ ├── RealtimeCallback.kt │ │ ├── RealtimeChannel.kt │ │ ├── RealtimeChannelBuilder.kt │ │ ├── RealtimeChannelImpl.kt │ │ ├── RealtimeExt.kt │ │ ├── RealtimeImpl.kt │ │ ├── RealtimeJoinPayload.kt │ │ ├── RealtimeMessage.kt │ │ ├── RealtimeTopic.kt │ │ ├── annotations │ │ └── ChannelDsl.kt │ │ ├── data │ │ ├── BroadcastApiBody.kt │ │ └── PostgresActionData.kt │ │ ├── event │ │ ├── RBroadcastEvent.kt │ │ ├── RCloseEvent.kt │ │ ├── RErrorEvent.kt │ │ ├── RPostgresChangesEvent.kt │ │ ├── RPostgresServerChangesEvent.kt │ │ ├── RPresenceDiffEvent.kt │ │ ├── RPresenceStateEvent.kt │ │ ├── RSystemEvent.kt │ │ ├── RSystemReplyEvent.kt │ │ ├── RTokenExpiredEvent.kt │ │ └── RealtimeEvent.kt │ │ └── websocket │ │ ├── KtorRealtimeWebsocket.kt │ │ ├── KtorRealtimeWebsocketFactory.kt │ │ ├── RealtimeWebsocket.kt │ │ └── RealtimeWebsocketFactory.kt │ └── commonTest │ └── kotlin │ ├── CallbackManagerTest.kt │ ├── RealtimeChannelTest.kt │ ├── RealtimeEventTest.kt │ ├── RealtimeExtTest.kt │ ├── RealtimeTest.kt │ ├── RealtimeTestUtils.kt │ ├── RealtimeTopicTest.kt │ └── RealtimeWSMock.kt ├── Storage ├── README.md ├── build.gradle.kts └── src │ ├── androidAndJvmMain │ └── kotlin │ │ └── io │ │ └── github │ │ └── jan │ │ └── supabase │ │ └── storage │ │ ├── JvmUtils.kt │ │ └── ResumableUtils.kt │ ├── androidMain │ ├── AndroidManifest.xml │ └── kotlin │ │ └── io │ │ └── github │ │ └── jan │ │ └── supabase │ │ └── storage │ │ ├── AndroidUtils.kt │ │ ├── Context.kt │ │ └── ResumableAndroidUtils.kt │ ├── commonMain │ └── kotlin │ │ └── io │ │ └── github │ │ └── jan │ │ └── supabase │ │ └── storage │ │ ├── Bucket.kt │ │ ├── BucketApi.kt │ │ ├── BucketApiImpl.kt │ │ ├── BucketBuilder.kt │ │ ├── BucketListFilter.kt │ │ ├── DownloadOptionBuilder.kt │ │ ├── FileObject.kt │ │ ├── FileSizeLimit.kt │ │ ├── FileUploadResponse.kt │ │ ├── FlowExtension.kt │ │ ├── ImageTransformation.kt │ │ ├── NetworkStatus.kt │ │ ├── SignedUrl.kt │ │ ├── Storage.kt │ │ ├── StorageErrorResponse.kt │ │ ├── StorageItem.kt │ │ ├── UploadData.kt │ │ ├── UploadOptionBuilder.kt │ │ ├── UploadSignedUrl.kt │ │ ├── Utils.kt │ │ └── resumable │ │ ├── Fingerprint.kt │ │ ├── MemoryResumableCache.kt │ │ ├── ResumableCache.kt │ │ ├── ResumableClient.kt │ │ ├── ResumableUpload.kt │ │ ├── ResumableUploadState.kt │ │ └── StreamContent.kt │ ├── commonTest │ └── kotlin │ │ ├── BucketApiFlowTest.kt │ │ ├── BucketApiTest.kt │ │ ├── BucketListFilterTest.kt │ │ └── StorageTest.kt │ ├── linuxMain │ └── kotlin │ │ └── io │ │ └── github │ │ └── jan │ │ └── supabase │ │ └── storage │ │ └── resumable │ │ └── ResumableCacheUtil.kt │ └── settingsMain │ └── kotlin │ └── io │ └── github │ └── jan │ └── supabase │ └── storage │ └── resumable │ └── SettingsResumableCache.kt ├── Supabase ├── README.md ├── build.gradle.kts └── src │ ├── androidMain │ ├── AndroidManifest.xml │ └── kotlin │ │ └── io │ │ └── github │ │ └── jan │ │ └── supabase │ │ ├── DefaultDispatcher.kt │ │ ├── PlatformTarget.android.kt │ │ └── PlatformTarget.kt │ ├── androidUnitTest │ └── kotlin │ │ └── PlatformTargetTest.kt │ ├── appleMain │ └── kotlin │ │ └── io │ │ └── supabase │ │ └── supabase │ │ └── Utils.kt │ ├── commonMain │ └── kotlin │ │ └── io │ │ └── github │ │ └── jan │ │ └── supabase │ │ ├── AccessTokenProvider.kt │ │ ├── DefaultDispatcher.kt │ │ ├── PlatformTarget.kt │ │ ├── SupabaseClient.kt │ │ ├── SupabaseClientBuilder.kt │ │ ├── SupabaseClientConfig.kt │ │ ├── SupabaseSerializer.kt │ │ ├── Utils.kt │ │ ├── annotations │ │ ├── SupabaseDsl.kt │ │ ├── SupabaseExperimental.kt │ │ └── SupabaseInternal.kt │ │ ├── collections │ │ ├── AtomicMutableList.kt │ │ └── AtomicMutableMap.kt │ │ ├── exceptions │ │ ├── HttpRequestException.kt │ │ ├── RestException.kt │ │ └── SupabaseEncodingException.kt │ │ ├── logging │ │ ├── LogLevel.kt │ │ └── SupabaseLogger.kt │ │ ├── network │ │ ├── KtorSupabaseHttpClient.kt │ │ ├── SupabaseApi.kt │ │ └── SupabaseHttpClient.kt │ │ ├── plugins │ │ ├── CustomSerializationConfig.kt │ │ ├── CustomSerializationPlugin.kt │ │ ├── MainPlugin.kt │ │ ├── PluginManager.kt │ │ ├── SerializableData.kt │ │ ├── SupabasePlugin.kt │ │ └── SupabasePluginProvider.kt │ │ └── serializer │ │ └── KotlinXSerializer.kt │ ├── commonTest │ └── kotlin │ │ └── PlatformTargetTest.kt │ ├── iosMain │ └── kotlin │ │ └── io │ │ └── github │ │ └── jan │ │ └── supabase │ │ ├── DefaultDispatcher.kt │ │ ├── PlatformTarget.ios.kt │ │ └── PlatformTarget.kt │ ├── iosTest │ └── kotlin │ │ └── PlatformTargetTest.kt │ ├── jsMain │ └── kotlin │ │ └── io │ │ └── github │ │ └── jan │ │ └── supabase │ │ ├── DefaultDispatcher.kt │ │ ├── PlatformTarget.js.kt │ │ └── PlatformTarget.kt │ ├── jsTest │ └── kotlin │ │ └── PlatformTargetTest.kt │ ├── jvmMain │ └── kotlin │ │ └── io │ │ └── github │ │ └── jan │ │ └── supabase │ │ ├── DefaultDispatcher.kt │ │ ├── PlatformTarget.jvm.kt │ │ └── PlatformTarget.kt │ ├── jvmTest │ └── kotlin │ │ └── PlatformTargetTest.kt │ ├── linuxMain │ └── kotlin │ │ └── io │ │ └── github │ │ └── jan │ │ └── supabase │ │ ├── DefaultDispatcher.kt │ │ ├── PlatformTarget.kt │ │ └── PlatformTarget.linux.kt │ ├── linuxTest │ └── kotlin │ │ └── PlatformTargetTest.kt │ ├── macosMain │ └── kotlin │ │ └── io │ │ └── github │ │ └── jan │ │ └── supabase │ │ ├── DefaultDispatcher.kt │ │ ├── PlatformTarget.kt │ │ └── PlatformTarget.macos.kt │ ├── macosTest │ └── kotlin │ │ └── PlatformTargetTest.kt │ ├── mingwMain │ └── kotlin │ │ └── io │ │ └── github │ │ └── jan │ │ └── supabase │ │ └── PlatformTarget.mingw.kt │ ├── mingwX64Main │ └── kotlin │ │ └── io │ │ └── github │ │ └── jan │ │ └── supabase │ │ ├── CurrentPlatformTarget.kt │ │ └── DefaultDispatcher.kt │ ├── mingwX64Test │ └── kotlin │ │ └── PlatformTargetTest.kt │ ├── tvosMain │ └── kotlin │ │ └── io │ │ └── github │ │ └── jan │ │ └── supabase │ │ ├── DefaultDispatcher.kt │ │ ├── PlatformTarget.kt │ │ └── PlatformTarget.tvos.kt │ ├── tvosTest │ └── kotlin │ │ └── PlatformTargetTest.kt │ ├── wasmJsMain │ └── kotlin │ │ └── io │ │ └── github │ │ └── jan │ │ └── supabase │ │ ├── DefaultDispatcher.kt │ │ └── PlatformTarget.wasmJs.kt │ ├── wasmJsTest │ └── kotlin │ │ └── PlatformTargetTest.kt │ ├── watchosMain │ └── kotlin │ │ └── io │ │ └── github │ │ └── jan │ │ └── supabase │ │ ├── DefaultDispatcher.kt │ │ ├── PlatformTarget.kt │ │ └── PlatformTarget.watchos.kt │ └── watchosTest │ └── kotlin │ └── PlatformTargetTest.kt ├── TROUBLESHOOTING.md ├── bom └── build.gradle.kts ├── build.gradle.kts ├── buildSrc ├── build.gradle.kts ├── settings.gradle.kts └── src │ └── main │ └── kotlin │ ├── Android.kt │ ├── Detekt.kt │ ├── Dokka.kt │ ├── KotlinPlugin.kt │ ├── KotlinTargets.kt │ ├── Modules.kt │ ├── PowerAssert.kt │ ├── Publishing.kt │ └── TargetHierarchy.kt ├── demos ├── android-login │ ├── .gitignore │ ├── README.md │ ├── android │ │ ├── build.gradle.kts │ │ └── src │ │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ └── java │ │ │ └── io │ │ │ └── github │ │ │ └── jan │ │ │ └── supabase │ │ │ └── android │ │ │ ├── MainActivity.kt │ │ │ └── MainApplication.kt │ ├── build.gradle.kts │ ├── buildSrc │ │ ├── build.gradle.kts │ │ └── src │ │ │ └── main │ │ │ └── kotlin │ │ │ └── Versions.kt │ ├── common │ │ ├── build.gradle.kts │ │ └── src │ │ │ ├── androidMain │ │ │ ├── AndroidManifest.xml │ │ │ └── kotlin │ │ │ │ └── io │ │ │ │ └── github │ │ │ │ └── jan │ │ │ │ └── supabase │ │ │ │ └── common │ │ │ │ ├── MPViewModel.kt │ │ │ │ └── di │ │ │ │ ├── platformGoTrueConfig.kt │ │ │ │ └── viewModel.kt │ │ │ └── commonMain │ │ │ └── kotlin │ │ │ └── io │ │ │ └── github │ │ │ └── jan │ │ │ └── supabase │ │ │ └── common │ │ │ ├── AppViewModel.kt │ │ │ ├── di │ │ │ ├── koin.kt │ │ │ ├── supabaseModule.kt │ │ │ └── viewModelModule.kt │ │ │ └── ui │ │ │ ├── components │ │ │ ├── OAuthWebView.kt │ │ │ └── PasswordField.kt │ │ │ └── screen │ │ │ ├── LoginScreen.kt │ │ │ └── OAuthScreen.kt │ ├── gradle.properties │ ├── gradle │ │ ├── libs.versions.toml │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── kotlin-js-store │ │ └── yarn.lock │ └── settings.gradle.kts └── multiplatform-deeplinks │ ├── .gitignore │ ├── README.md │ ├── android │ ├── build.gradle.kts │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ └── java │ │ └── io │ │ └── github │ │ └── jan │ │ └── supabase │ │ └── android │ │ ├── MainActivity.kt │ │ └── MainApplication.kt │ ├── build.gradle.kts │ ├── buildSrc │ ├── build.gradle.kts │ └── src │ │ └── main │ │ └── kotlin │ │ └── Versions.kt │ ├── common │ ├── build.gradle.kts │ └── src │ │ ├── androidMain │ │ ├── AndroidManifest.xml │ │ └── kotlin │ │ │ └── io │ │ │ └── github │ │ │ └── jan │ │ │ └── supabase │ │ │ └── common │ │ │ ├── MPViewModel.kt │ │ │ ├── di │ │ │ ├── platformGoTrueConfig.kt │ │ │ └── viewModel.kt │ │ │ └── ui │ │ │ └── components │ │ │ └── AlertDialog.kt │ │ ├── commonMain │ │ └── kotlin │ │ │ └── io │ │ │ └── github │ │ │ └── jan │ │ │ └── supabase │ │ │ └── common │ │ │ ├── App.kt │ │ │ ├── AppViewModel.kt │ │ │ ├── di │ │ │ ├── koin.kt │ │ │ ├── netModule.kt │ │ │ ├── supabaseModule.kt │ │ │ └── viewModelModule.kt │ │ │ └── ui │ │ │ ├── components │ │ │ ├── AlertDialog.kt │ │ │ ├── GoogleButton.kt │ │ │ └── PasswordField.kt │ │ │ ├── icons │ │ │ └── Google.kt │ │ │ └── screen │ │ │ ├── AppScreen.kt │ │ │ └── LoginScreen.kt │ │ └── desktopMain │ │ └── kotlin │ │ └── io │ │ └── github │ │ └── jan │ │ └── supabase │ │ └── common │ │ ├── MPViewModel.kt │ │ ├── di │ │ ├── platformGoTrueConfig.kt │ │ └── viewModel.kt │ │ └── ui │ │ └── components │ │ └── AlertDialog.kt │ ├── desktop │ ├── build.gradle.kts │ ├── conveyor.conf │ ├── icons │ │ └── icon.svg │ └── src │ │ └── main │ │ └── kotlin │ │ └── Main.kt │ ├── gradle.properties │ ├── gradle │ ├── libs.versions.toml │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── kotlin-js-store │ └── yarn.lock │ └── settings.gradle.kts ├── detekt.yml ├── gradle.properties ├── gradle ├── libs.versions.toml └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── plugins ├── ApolloGraphQL │ ├── README.md │ ├── build.gradle.kts │ └── src │ │ ├── androidMain │ │ └── AndroidManifest.xml │ │ ├── commonMain │ │ └── kotlin │ │ │ └── io │ │ │ └── github │ │ │ └── jan │ │ │ └── supabase │ │ │ └── graphql │ │ │ ├── ApolloHttpInterceptor.kt │ │ │ └── GraphQL.kt │ │ └── commonTest │ │ └── kotlin │ │ ├── ApolloHttpInterceptorTest.kt │ │ └── GraphQLTest.kt ├── Coil3Integration │ ├── README.md │ ├── build.gradle.kts │ └── src │ │ ├── androidMain │ │ └── AndroidManifest.xml │ │ └── commonMain │ │ └── kotlin │ │ └── io │ │ └── github │ │ └── jan │ │ └── supabase │ │ └── coil │ │ ├── Coil3Integration.kt │ │ └── SupabaseStorageFetcher.kt ├── CoilIntegration │ ├── README.md │ ├── build.gradle.kts │ └── src │ │ ├── androidMain │ │ └── AndroidManifest.xml │ │ └── commonMain │ │ └── kotlin │ │ └── io │ │ └── github │ │ └── jan │ │ └── supabase │ │ └── coil │ │ ├── CoilIntegration.kt │ │ └── SupabaseStorageFetcher.kt ├── ComposeAuth │ ├── README.md │ ├── build.gradle.kts │ └── src │ │ ├── androidMain │ │ ├── AndroidManifest.xml │ │ └── kotlin │ │ │ └── io │ │ │ └── github │ │ │ └── jan │ │ │ └── supabase │ │ │ └── compose │ │ │ └── auth │ │ │ ├── SupabaseInitializer.kt │ │ │ ├── Utils.kt │ │ │ └── composable │ │ │ ├── AppleAuth.kt │ │ │ └── GoogleAuth.kt │ │ ├── appleMain │ │ └── kotlin │ │ │ └── io │ │ │ └── github │ │ │ └── jan │ │ │ └── supabase │ │ │ └── compose │ │ │ └── auth │ │ │ └── composable │ │ │ ├── AppleAuth.kt │ │ │ └── GoogleAuth.kt │ │ ├── commonMain │ │ └── kotlin │ │ │ └── io │ │ │ └── github │ │ │ └── jan │ │ │ └── supabase │ │ │ └── compose │ │ │ └── auth │ │ │ ├── AppleLoginConfig.kt │ │ │ ├── ComposeAuth.kt │ │ │ ├── DefaultBehavior.kt │ │ │ ├── GoogleLoginConfig.kt │ │ │ ├── Utils.common.kt │ │ │ └── composable │ │ │ ├── NativeAppleAuth.kt │ │ │ ├── NativeGoogleAuth.kt │ │ │ ├── NativeSignInResult.kt │ │ │ └── NativeSignInState.kt │ │ └── noDefaultMain │ │ └── kotlin │ │ └── io │ │ └── github │ │ └── jan │ │ └── supabase │ │ └── compose │ │ └── auth │ │ └── composable │ │ ├── AppleAuth.kt │ │ └── GoogleAuth.kt ├── ComposeAuthUI │ ├── README.md │ ├── build.gradle.kts │ └── src │ │ ├── androidMain │ │ ├── AndroidManifest.xml │ │ └── kotlin │ │ │ └── io │ │ │ └── github │ │ │ └── jan │ │ │ └── supabase │ │ │ └── compose │ │ │ └── auth │ │ │ └── ui │ │ │ └── AndroidSvgPainter.kt │ │ ├── commonMain │ │ └── kotlin │ │ │ └── io │ │ │ └── github │ │ │ └── jan │ │ │ └── supabase │ │ │ └── compose │ │ │ └── auth │ │ │ └── ui │ │ │ ├── AuthIcons.kt │ │ │ ├── AuthState.kt │ │ │ ├── FormComponent.kt │ │ │ ├── FormValidator.kt │ │ │ ├── ProviderButton.kt │ │ │ ├── ProviderIcons.kt │ │ │ ├── SvgPainter.kt │ │ │ ├── annotations │ │ │ └── AuthUiExperimental.kt │ │ │ ├── email │ │ │ └── EmailField.kt │ │ │ ├── password │ │ │ ├── PasswordField.kt │ │ │ └── PasswordRule.kt │ │ │ └── phone │ │ │ ├── PhoneField.kt │ │ │ └── PhoneVisualTransformation.kt │ │ ├── jvmMain │ │ └── kotlin │ │ │ └── io │ │ │ └── github │ │ │ └── jan │ │ │ └── supabase │ │ │ └── compose │ │ │ └── auth │ │ │ └── ui │ │ │ └── SvgPainterJvm.kt │ │ └── nonJvmMain │ │ └── kotlin │ │ └── io │ │ └── github │ │ └── jan │ │ └── supabase │ │ └── compose │ │ └── auth │ │ └── ui │ │ ├── DrawCache.kt │ │ └── SvgPainterNonJvm.kt └── ImageLoaderIntegration │ ├── README.md │ ├── build.gradle.kts │ └── src │ ├── androidMain │ └── AndroidManifest.xml │ └── commonMain │ └── kotlin │ └── io │ └── github │ └── jan │ └── supabase │ └── imageloader │ ├── ImageLoaderIntegration.kt │ └── SupabaseStorageFetcher.kt ├── sample ├── chat-demo-mpp │ ├── README.md │ ├── android │ │ ├── build.gradle.kts │ │ └── src │ │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ └── java │ │ │ └── io │ │ │ └── github │ │ │ └── jan │ │ │ └── supabase │ │ │ └── android │ │ │ ├── MainActivity.kt │ │ │ └── MainApplication.kt │ ├── chatdemoios │ │ └── chatdemoios.xcodeproj │ │ │ └── project.xcworkspace │ │ │ └── contents.xcworkspacedata │ ├── common │ │ ├── build.gradle.kts │ │ └── src │ │ │ ├── androidMain │ │ │ ├── AndroidManifest.xml │ │ │ └── kotlin │ │ │ │ └── io │ │ │ │ └── github │ │ │ │ └── jan │ │ │ │ └── supabase │ │ │ │ └── common │ │ │ │ ├── MPViewModel.kt │ │ │ │ └── di │ │ │ │ ├── platformGoTrueConfig.kt │ │ │ │ └── viewModel.kt │ │ │ ├── commonMain │ │ │ └── kotlin │ │ │ │ └── io │ │ │ │ └── github │ │ │ │ └── jan │ │ │ │ └── supabase │ │ │ │ └── common │ │ │ │ ├── App.kt │ │ │ │ ├── ChatViewModel.kt │ │ │ │ ├── di │ │ │ │ ├── koin.kt │ │ │ │ ├── netModule.kt │ │ │ │ ├── supabaseModule.kt │ │ │ │ └── viewModelModule.kt │ │ │ │ ├── net │ │ │ │ ├── AuthApi.kt │ │ │ │ └── MessageApi.kt │ │ │ │ └── ui │ │ │ │ ├── components │ │ │ │ ├── MessageCard.kt │ │ │ │ ├── OTPDialog.kt │ │ │ │ ├── PasswordChangeDialog.kt │ │ │ │ ├── PasswordField.kt │ │ │ │ └── PasswordRecoverDialog.kt │ │ │ │ ├── icons │ │ │ │ └── Google.kt │ │ │ │ └── screen │ │ │ │ ├── ChatScreen.kt │ │ │ │ └── LoginScreen.kt │ │ │ ├── desktopMain │ │ │ └── kotlin │ │ │ │ └── io │ │ │ │ └── github │ │ │ │ └── jan │ │ │ │ └── supabase │ │ │ │ └── common │ │ │ │ ├── MPViewModel.kt │ │ │ │ └── di │ │ │ │ ├── platformGoTrueConfig.kt │ │ │ │ └── viewModel.kt │ │ │ ├── iosMain │ │ │ └── kotlin │ │ │ │ └── io │ │ │ │ └── github │ │ │ │ └── jan │ │ │ │ └── supabase │ │ │ │ └── common │ │ │ │ ├── App.kt │ │ │ │ ├── MPViewModel.kt │ │ │ │ └── di │ │ │ │ ├── platformGoTrueConfig.kt │ │ │ │ └── viewModel.kt │ │ │ └── jsMain │ │ │ └── kotlin │ │ │ └── io │ │ │ └── github │ │ │ └── jan │ │ │ └── supabase │ │ │ └── common │ │ │ ├── MPViewModel.kt │ │ │ └── di │ │ │ ├── platformGoTrueConfig.kt │ │ │ └── viewModel.kt │ ├── desktop │ │ ├── build.gradle.kts │ │ └── src │ │ │ └── jvmMain │ │ │ └── kotlin │ │ │ └── Main.kt │ ├── ios │ │ ├── chatdemoios.xcodeproj │ │ │ ├── project.pbxproj │ │ │ ├── project.xcworkspace │ │ │ │ ├── contents.xcworkspacedata │ │ │ │ ├── xcshareddata │ │ │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ │ │ └── WorkspaceSettings.xcsettings │ │ │ │ └── xcuserdata │ │ │ │ │ └── hieuvu.xcuserdatad │ │ │ │ │ ├── UserInterfaceState.xcuserstate │ │ │ │ │ └── WorkspaceSettings.xcsettings │ │ │ ├── xcshareddata │ │ │ │ └── xcschemes │ │ │ │ │ └── chatdemoios.xcscheme │ │ │ └── xcuserdata │ │ │ │ └── hieuvu.xcuserdatad │ │ │ │ └── xcschemes │ │ │ │ └── xcschememanagement.plist │ │ ├── chatdemoios.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ ├── xcshareddata │ │ │ │ └── IDEWorkspaceChecks.plist │ │ │ └── xcuserdata │ │ │ │ └── hieuvu.xcuserdatad │ │ │ │ └── UserInterfaceState.xcuserstate │ │ └── chatdemoios │ │ │ ├── Assets.xcassets │ │ │ ├── AccentColor.colorset │ │ │ │ └── Contents.json │ │ │ ├── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ │ └── Contents.json │ │ │ ├── ContentView.swift │ │ │ ├── Preview Content │ │ │ └── Preview Assets.xcassets │ │ │ │ └── Contents.json │ │ │ ├── chatdemoios.entitlements │ │ │ └── chatdemoiosApp.swift │ └── web │ │ ├── build.gradle.kts │ │ └── src │ │ └── jsMain │ │ ├── kotlin │ │ └── Main.kt │ │ └── resources │ │ ├── index.html │ │ └── styles.css ├── file-upload │ ├── README.md │ ├── android │ │ ├── build.gradle.kts │ │ └── src │ │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ └── java │ │ │ └── io │ │ │ └── github │ │ │ └── jan │ │ │ └── supabase │ │ │ └── android │ │ │ ├── MainActivity.kt │ │ │ └── MainApplication.kt │ ├── common │ │ ├── build.gradle.kts │ │ └── src │ │ │ ├── androidMain │ │ │ ├── AndroidManifest.xml │ │ │ └── kotlin │ │ │ │ └── io │ │ │ │ └── github │ │ │ │ └── jan │ │ │ │ └── supabase │ │ │ │ └── common │ │ │ │ ├── MPViewModel.kt │ │ │ │ ├── SupabaseInitializer.kt │ │ │ │ ├── Uploads.android.kt │ │ │ │ ├── di │ │ │ │ └── viewModel.kt │ │ │ │ ├── parseFileTreeFromPath.kt │ │ │ │ └── ui │ │ │ │ ├── components │ │ │ │ └── AlertDialog.kt │ │ │ │ └── utils │ │ │ │ ├── applyDragging.kt │ │ │ │ └── bytesToBitmap.kt │ │ │ ├── commonMain │ │ │ └── kotlin │ │ │ │ └── io │ │ │ │ └── github │ │ │ │ └── jan │ │ │ │ └── supabase │ │ │ │ └── common │ │ │ │ ├── App.kt │ │ │ │ ├── UploadViewModel.kt │ │ │ │ ├── Uploads.kt │ │ │ │ ├── Utils.kt │ │ │ │ ├── di │ │ │ │ ├── koin.kt │ │ │ │ ├── supabaseModule.kt │ │ │ │ └── viewModelModule.kt │ │ │ │ └── ui │ │ │ │ ├── components │ │ │ │ ├── AlertDialog.kt │ │ │ │ └── UploadCard.kt │ │ │ │ ├── screen │ │ │ │ └── UploadScreen.kt │ │ │ │ └── utils │ │ │ │ ├── Bitmap.kt │ │ │ │ ├── Dragging.kt │ │ │ │ └── FileSize.kt │ │ │ └── desktopMain │ │ │ └── kotlin │ │ │ └── io │ │ │ └── github │ │ │ └── jan │ │ │ └── supabase │ │ │ └── common │ │ │ ├── MPViewModel.kt │ │ │ ├── Uploads.desktop.kt │ │ │ ├── di │ │ │ └── viewModel.kt │ │ │ ├── parseFileTreeFromPath.kt │ │ │ └── ui │ │ │ ├── components │ │ │ └── AlertDialog.kt │ │ │ └── utils │ │ │ ├── applyDragging.kt │ │ │ └── bytesToBitmap.kt │ └── desktop │ │ ├── build.gradle.kts │ │ └── src │ │ └── jvmMain │ │ └── kotlin │ │ └── Main.kt └── multi-factor-auth │ ├── README.md │ ├── android │ ├── build.gradle.kts │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ └── java │ │ └── io │ │ └── github │ │ └── jan │ │ └── supabase │ │ └── android │ │ ├── MainActivity.kt │ │ └── MainApplication.kt │ ├── common │ ├── build.gradle.kts │ └── src │ │ ├── androidMain │ │ ├── AndroidManifest.xml │ │ └── kotlin │ │ │ └── io │ │ │ └── github │ │ │ └── jan │ │ │ └── supabase │ │ │ └── common │ │ │ ├── MPViewModel.kt │ │ │ ├── di │ │ │ ├── platformGoTrueConfig.kt │ │ │ └── viewModel.kt │ │ │ └── ui │ │ │ └── components │ │ │ ├── AlertDialog.kt │ │ │ └── QRCode.kt │ │ ├── commonMain │ │ └── kotlin │ │ │ └── io │ │ │ └── github │ │ │ └── jan │ │ │ └── supabase │ │ │ └── common │ │ │ ├── App.kt │ │ │ ├── AppViewModel.kt │ │ │ ├── di │ │ │ ├── koin.kt │ │ │ ├── netModule.kt │ │ │ ├── supabaseModule.kt │ │ │ └── viewModelModule.kt │ │ │ └── ui │ │ │ ├── components │ │ │ ├── AlertDialog.kt │ │ │ ├── GoogleButton.kt │ │ │ ├── PasswordField.kt │ │ │ └── QRCode.kt │ │ │ ├── icons │ │ │ └── Google.kt │ │ │ └── screen │ │ │ ├── AppScreen.kt │ │ │ ├── LoginScreen.kt │ │ │ ├── MfaLoginScreen.kt │ │ │ ├── MfaScreen.kt │ │ │ └── MfaSetupScreen.kt │ │ ├── desktopMain │ │ └── kotlin │ │ │ └── io │ │ │ └── github │ │ │ └── jan │ │ │ └── supabase │ │ │ └── common │ │ │ ├── MPViewModel.kt │ │ │ ├── di │ │ │ ├── platformGoTrueConfig.kt │ │ │ └── viewModel.kt │ │ │ └── ui │ │ │ └── components │ │ │ ├── AlertDialog.kt │ │ │ └── QRCode.kt │ │ └── jsMain │ │ └── kotlin │ │ └── io │ │ └── github │ │ └── jan │ │ └── supabase │ │ └── common │ │ ├── MPViewModel.kt │ │ ├── di │ │ ├── platformGoTrueConfig.kt │ │ └── viewModel.kt │ │ └── ui │ │ └── components │ │ ├── AlertDialog.kt │ │ └── QRCode.kt │ ├── desktop │ ├── build.gradle.kts │ └── src │ │ └── jvmMain │ │ └── kotlin │ │ └── Main.kt │ └── web │ ├── build.gradle.kts │ └── src │ └── jsMain │ ├── kotlin │ └── Main.kt │ └── resources │ ├── index.html │ └── styles.css ├── serializers ├── Jackson │ ├── README.md │ ├── build.gradle.kts │ └── src │ │ ├── commonMain │ │ └── kotlin │ │ │ └── io │ │ │ └── github │ │ │ └── jan │ │ │ └── supabase │ │ │ └── serializer │ │ │ └── JacksonSerializer.kt │ │ └── commonTest │ │ └── kotlin │ │ └── JacksonSerializerTest.kt └── Moshi │ ├── README.md │ ├── build.gradle.kts │ └── src │ ├── commonMain │ └── kotlin │ │ └── io │ │ └── github │ │ └── jan │ │ └── supabase │ │ └── serializer │ │ └── MoshiSerializer.kt │ └── commonTest │ └── kotlin │ └── MoshiSerializerTest.kt ├── settings.gradle.kts └── test-common ├── build.gradle.kts └── src ├── androidMain └── AndroidManifest.xml ├── commonMain └── kotlin │ └── io │ └── github │ └── jan │ └── supabase │ └── testing │ ├── KtorUtils.kt │ ├── RouteUtils.kt │ ├── SupabaseClientMock.kt │ └── TestUtils.kt └── commonTest └── kotlin ├── DummySerializer.kt ├── SupabaseClientTest.kt └── TestPlugin.kt /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | ko_fi: jantennert 2 | github: jan-tennert 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- 1 | name: Feature request 2 | description: Template for feature request 3 | title: '[Feature request]: ' 4 | labels: ["enhancement"] 5 | body: 6 | - type: checkboxes 7 | id: latest-version 8 | attributes: 9 | label: General Info 10 | options: 11 | - label: I installed the latest version of Supabase Kt 12 | required: true 13 | - label: I checked for similar feature requests 14 | required: true 15 | - type: textarea 16 | id: feature-request 17 | attributes: 18 | label: Feature request 19 | description: Please describe what you think should be added to Supabase Kt 20 | validations: 21 | required: true 22 | - type: textarea 23 | id: usecase 24 | attributes: 25 | label: Usecase 26 | description: 'Please provide an use case for your feature request' 27 | validations: 28 | required: false 29 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.yml: -------------------------------------------------------------------------------- 1 | name: Question 2 | description: Template for questions 3 | title: '[Question]: ' 4 | labels: ["question"] 5 | body: 6 | - type: checkboxes 7 | id: general 8 | attributes: 9 | label: General info 10 | options: 11 | - label: I checked the [troubleshooting](https://github.com/supabase-community/supabase-kt/blob/master/TROUBLESHOOTING.md) page for similar problems 12 | required: true 13 | - type: textarea 14 | id: question 15 | attributes: 16 | label: What is your question? 17 | description: Please ask your question 18 | validations: 19 | required: true 20 | - type: textarea 21 | id: logs 22 | attributes: 23 | label: Relevant log output (optional) 24 | description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks. 25 | render: shell 26 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "gradle" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "weekly" 12 | reviewers: 13 | - "jan-tennert" 14 | assignees: 15 | - "jan-tennert" 16 | -------------------------------------------------------------------------------- /.github/images/desktop_supabase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supabase-community/supabase-kt/8d9207594a4f651f3f8ff2e4a5082787d7f15deb/.github/images/desktop_supabase.png -------------------------------------------------------------------------------- /.github/images/img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supabase-community/supabase-kt/8d9207594a4f651f3f8ff2e4a5082787d7f15deb/.github/images/img.png -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build project 2 | 3 | on: 4 | push: 5 | branches: 6 | - 'master' 7 | 8 | concurrency: 9 | group: environment-${{ github.ref }} 10 | cancel-in-progress: true 11 | 12 | jobs: 13 | build: 14 | runs-on: macos-latest 15 | steps: 16 | - name: Checkout sources 17 | uses: actions/checkout@v4 18 | - name: Set up JDK 17 19 | uses: actions/setup-java@v4 20 | with: 21 | java-version: '17' 22 | distribution: 'temurin' 23 | - name: Setup Gradle 24 | uses: gradle/actions/setup-gradle@v4.0.1 25 | with: 26 | cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }} 27 | cache-read-only: false 28 | - name: Build supabase-kt 29 | run: ./gradlew -DLibrariesOnly=true assemble --stacktrace --configuration-cache --scan 30 | -------------------------------------------------------------------------------- /.github/workflows/cache.yml: -------------------------------------------------------------------------------- 1 | name: Clear all Github actions caches 2 | on: 3 | workflow_dispatch: 4 | 5 | permissions: 6 | actions: write 7 | 8 | jobs: 9 | clear-cache: 10 | name: Delete all caches 11 | runs-on: ubuntu-20.04 12 | steps: 13 | - name: Clear caches 14 | uses: easimon/wipe-cache@main 15 | with: 16 | github-token: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.github/workflows/labeler.yml: -------------------------------------------------------------------------------- 1 | name: "Pull Request Labeler" 2 | on: 3 | - pull_request_target 4 | 5 | jobs: 6 | labeler: 7 | permissions: 8 | contents: read 9 | pull-requests: write 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/labeler@v5 13 | with: 14 | sync-labels: true -------------------------------------------------------------------------------- /.github/workflows/samples.yml: -------------------------------------------------------------------------------- 1 | name: Build samples 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - '*' 7 | push: 8 | branches: 9 | - 'master' 10 | 11 | jobs: 12 | build: 13 | strategy: 14 | matrix: 15 | sample: [ 16 | 'chat-demo-mpp', 17 | 'file-upload', 18 | 'multi-factor-auth' 19 | ] 20 | runs-on: macos-latest 21 | steps: 22 | - name: Checkout sources 23 | uses: actions/checkout@v4 24 | - name: Set up JDK 17 25 | uses: actions/setup-java@v4 26 | with: 27 | java-version: '17' 28 | distribution: 'temurin' 29 | - name: Setup Gradle 30 | uses: gradle/actions/setup-gradle@v4.0.1 31 | with: 32 | cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }} 33 | cache-read-only: ${{ github.ref != 'refs/heads/master' }} 34 | - name: Build sample ${{ matrix.sample }} 35 | run: ./gradlew :sample:${{ matrix.sample }}:buildAll --stacktrace --configuration-cache -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Project exclude paths 2 | **/.gradle/** 3 | **/build/** 4 | /test/ 5 | .idea 6 | local.properties 7 | .kotlin 8 | kotlin-js-store 9 | -------------------------------------------------------------------------------- /Auth/src/androidMain/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 11 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Auth/src/androidMain/kotlin/io/github/jan/supabase/auth/RedirectUrl.android.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.auth 2 | 3 | import io.github.jan.supabase.annotations.SupabaseInternal 4 | 5 | @SupabaseInternal 6 | internal actual fun Auth.defaultPlatformRedirectUrl(): String? = config.deepLinkOrNull -------------------------------------------------------------------------------- /Auth/src/androidMain/kotlin/io/github/jan/supabase/auth/Utils.android.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("RedundantSuspendModifier") 2 | package io.github.jan.supabase.auth 3 | 4 | import android.net.Uri 5 | import io.github.jan.supabase.SupabaseClient 6 | import io.github.jan.supabase.auth.user.UserSession 7 | 8 | internal actual suspend fun SupabaseClient.openExternalUrl(url: String) { 9 | openUrl(Uri.parse(url), auth.config.defaultExternalAuthAction) 10 | } 11 | 12 | internal actual suspend fun Auth.startExternalAuth( 13 | redirectUrl: String?, 14 | getUrl: suspend (redirectTo: String?) -> String, 15 | onSessionSuccess: suspend (UserSession) -> Unit 16 | ) { 17 | config.urlLauncher.openUrl(supabaseClient, getUrl(redirectUrl)) 18 | } -------------------------------------------------------------------------------- /Auth/src/androidMain/kotlin/io/github/jan/supabase/auth/providers/ExternalAuthConfig.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.auth.providers 2 | 3 | /** 4 | * Configuration for external authentication providers like Google, Twitter, etc. 5 | */ 6 | actual class ExternalAuthConfig: ExternalAuthConfigDefaults() -------------------------------------------------------------------------------- /Auth/src/androidUnitTest/kotlin/platformSettings.kt: -------------------------------------------------------------------------------- 1 | import io.github.jan.supabase.auth.AuthConfig 2 | 3 | actual fun AuthConfig.platformSettings() { 4 | enableLifecycleCallbacks = false 5 | } -------------------------------------------------------------------------------- /Auth/src/appleMain/kotlin/io/github/jan/supabase/auth/RedirectUrl.apple.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.auth 2 | 3 | import io.github.jan.supabase.annotations.SupabaseInternal 4 | 5 | @SupabaseInternal 6 | internal actual fun Auth.defaultPlatformRedirectUrl(): String? = config.deepLinkOrNull -------------------------------------------------------------------------------- /Auth/src/appleMain/kotlin/io/github/jan/supabase/auth/Utils.apple.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.auth 2 | 3 | import io.github.jan.supabase.SupabaseClient 4 | import io.github.jan.supabase.auth.providers.openUrl 5 | import platform.Foundation.NSURL 6 | 7 | internal actual suspend fun SupabaseClient.openExternalUrl(url: String) { 8 | openUrl(NSURL(string = url)) 9 | } -------------------------------------------------------------------------------- /Auth/src/appleMain/kotlin/io/github/jan/supabase/auth/providers/ExternalAuthConfig.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.auth.providers 2 | 3 | /** 4 | * Configuration for external authentication providers like Google, Twitter, etc. 5 | */ 6 | actual class ExternalAuthConfig: ExternalAuthConfigDefaults() -------------------------------------------------------------------------------- /Auth/src/appleMain/kotlin/io/github/jan/supabase/auth/providers/OAuthProvider.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.auth.providers 2 | 3 | import platform.Foundation.NSURL 4 | 5 | internal expect fun openUrl(url: NSURL) -------------------------------------------------------------------------------- /Auth/src/appleMain/kotlin/io/github/jan/supabase/auth/setupPlatform.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.auth 2 | 3 | import io.github.jan.supabase.annotations.SupabaseInternal 4 | 5 | @SupabaseInternal 6 | actual fun Auth.setupPlatform() = Unit -------------------------------------------------------------------------------- /Auth/src/appleTest/kotlin/platformSettings.kt: -------------------------------------------------------------------------------- 1 | import io.github.jan.supabase.auth.AuthConfig 2 | 3 | actual fun AuthConfig.platformSettings() = Unit -------------------------------------------------------------------------------- /Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/JsonUtils.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.auth 2 | 3 | import kotlinx.serialization.json.JsonObjectBuilder 4 | import kotlinx.serialization.json.put 5 | import kotlinx.serialization.json.putJsonObject 6 | 7 | internal fun JsonObjectBuilder.putCaptchaToken(token: String) { 8 | putJsonObject("gotrue_meta_security") { 9 | put("captcha_token", token) 10 | } 11 | } 12 | 13 | internal fun JsonObjectBuilder.putCodeChallenge(codeChallenge: String) { 14 | put("code_challenge", codeChallenge) 15 | put("code_challenge_method", PKCEConstants.CHALLENGE_METHOD) 16 | } -------------------------------------------------------------------------------- /Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/MinimalConfig.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.auth 2 | 3 | /** 4 | * Applies minimal configuration to the [AuthConfig]. This is useful for server side applications, where you don't need to store the session or code verifier. 5 | * @see AuthConfigDefaults 6 | */ 7 | fun AuthConfigDefaults.minimalConfig() { 8 | this.alwaysAutoRefresh = false 9 | this.autoLoadFromStorage = false 10 | this.autoSaveToStorage = false 11 | this.sessionManager = MemorySessionManager() 12 | this.codeVerifierCache = MemoryCodeVerifierCache() 13 | this.enableLifecycleCallbacks = false 14 | } 15 | 16 | -------------------------------------------------------------------------------- /Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/PKCE.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("MatchingDeclarationName") 2 | package io.github.jan.supabase.auth 3 | 4 | import okio.ByteString.Companion.toByteString 5 | import org.kotlincrypto.random.CryptoRand 6 | import kotlin.io.encoding.Base64 7 | import kotlin.io.encoding.ExperimentalEncodingApi 8 | 9 | internal object PKCEConstants { 10 | const val VERIFIER_LENGTH = 64 11 | const val CHALLENGE_METHOD = "s256" 12 | } 13 | 14 | @OptIn(ExperimentalEncodingApi::class) 15 | internal fun generateCodeVerifier(): String { 16 | val bytes = ByteArray(PKCEConstants.VERIFIER_LENGTH) 17 | CryptoRand.nextBytes(bytes) 18 | return Base64.UrlSafe.encode(bytes) 19 | } 20 | 21 | @OptIn(ExperimentalEncodingApi::class) 22 | internal fun generateCodeChallenge(codeVerifier: String): String { 23 | val byteString = codeVerifier.encodeToByteArray().toByteString() 24 | val hash = byteString.sha256() 25 | return Base64.UrlSafe.encode(hash.toByteArray()).replace("=", "") 26 | } -------------------------------------------------------------------------------- /Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/PostgrestFilterDSL.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.auth 2 | 3 | /** 4 | * Used to mark Postgrest filter DSL functions 5 | */ 6 | @Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.TYPE) 7 | @DslMarker 8 | annotation class PostgrestFilterDSL -------------------------------------------------------------------------------- /Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/RedirectUrl.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.auth 2 | 3 | import io.github.jan.supabase.annotations.SupabaseInternal 4 | 5 | @SupabaseInternal 6 | internal expect fun Auth.defaultPlatformRedirectUrl(): String? 7 | 8 | internal fun Auth.defaultRedirectUrl(): String? { 9 | return config.defaultRedirectUrl ?: defaultPlatformRedirectUrl() 10 | } -------------------------------------------------------------------------------- /Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/SignOutScope.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.auth 2 | 3 | /** 4 | * Represents the scope of a sign-out action. 5 | * 6 | * The sign-out scope determines the scope of the sign-out action being performed. 7 | */ 8 | enum class SignOutScope { 9 | /** 10 | * Sign-out action applies to all sessions across the entire system. 11 | */ 12 | GLOBAL, 13 | 14 | /** 15 | * Sign-out action applies only to the current session. 16 | */ 17 | LOCAL, 18 | 19 | /** 20 | * Sign-out action applies to other sessions, excluding the current session. 21 | */ 22 | OTHERS 23 | } -------------------------------------------------------------------------------- /Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/UrlLauncher.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.auth 2 | 3 | import io.github.jan.supabase.SupabaseClient 4 | import io.github.jan.supabase.annotations.SupabaseExperimental 5 | 6 | /** 7 | * A [UrlLauncher] is used to open a URL in the system browser. 8 | */ 9 | @SupabaseExperimental 10 | fun interface UrlLauncher { 11 | 12 | /** 13 | * Open the given URL in the system browser. 14 | * @param url The URL to open. 15 | */ 16 | suspend fun openUrl(supabase: SupabaseClient, url: String) 17 | 18 | companion object { 19 | 20 | /** 21 | * Default implementation of [UrlLauncher] that opens the URL in the system browser. 22 | */ 23 | val DEFAULT = UrlLauncher { supabase, url -> 24 | supabase.openExternalUrl(url) 25 | } 26 | 27 | } 28 | 29 | } -------------------------------------------------------------------------------- /Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/Utils.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.auth 2 | 3 | import io.github.jan.supabase.SupabaseClient 4 | import io.github.jan.supabase.auth.user.UserSession 5 | 6 | internal fun invalidArg(message: String): Nothing = throw IllegalArgumentException(message) 7 | 8 | internal expect suspend fun SupabaseClient.openExternalUrl(url: String) 9 | 10 | internal expect suspend fun Auth.startExternalAuth( 11 | redirectUrl: String?, 12 | getUrl: suspend (redirectTo: String?) -> String, 13 | onSessionSuccess: suspend (UserSession) -> Unit 14 | ) -------------------------------------------------------------------------------- /Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/exception/AuthSessionMissingException.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.auth.exception 2 | 3 | import io.ktor.client.statement.HttpResponse 4 | 5 | /** 6 | * Exception thrown when a session is not found. 7 | */ 8 | class AuthSessionMissingException(response: HttpResponse): AuthRestException( 9 | errorCode = CODE, 10 | response = response, 11 | errorDescription = "Session not found. This can happen if the user was logged out or deleted." 12 | ) { 13 | 14 | internal companion object { 15 | const val CODE = "session_not_found" 16 | } 17 | 18 | } -------------------------------------------------------------------------------- /Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/exception/AuthWeakPasswordException.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.auth.exception 2 | 3 | import io.ktor.client.statement.HttpResponse 4 | 5 | /** 6 | * Exception thrown on sign-up if the password is too weak 7 | * @param description The description of the exception. 8 | * @param reasons The reasons why the password is weak. 9 | */ 10 | class AuthWeakPasswordException( 11 | description: String, 12 | response: HttpResponse, 13 | val reasons: List 14 | ) : AuthRestException( 15 | CODE, 16 | description, 17 | response 18 | ) { 19 | 20 | internal companion object { 21 | const val CODE = "weak_password" 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/mfa/MfaChallenge.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.auth.mfa 2 | 3 | import kotlinx.datetime.Instant 4 | import kotlinx.serialization.SerialName 5 | import kotlinx.serialization.Serializable 6 | 7 | /** 8 | * A challenge to verify the user's identity. 9 | * @property id The id of the challenge. 10 | * @property factorType Factor Type which generated the challenge. 11 | */ 12 | @Serializable 13 | data class MfaChallenge(val id: String, @SerialName("type") val factorType: String) { 14 | 15 | @SerialName("expires_at") private val expiresAtSeconds: Long = 0 16 | 17 | /** 18 | * Timestamp in UNIX seconds when this challenge will no longer be usable. 19 | */ 20 | val expiresAt: Instant 21 | get() = Instant.fromEpochSeconds(expiresAtSeconds) 22 | 23 | } 24 | -------------------------------------------------------------------------------- /Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/mfa/MfaFactor.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.auth.mfa 2 | 3 | /** 4 | * Represents an enrolled MFA Factor 5 | * @param id The ID of the factor 6 | * @param type The type of the factor 7 | * @param data Additional data of the factor (like QR-Code for TOTP) 8 | */ 9 | data class MfaFactor( 10 | val id: String, 11 | val type: String, 12 | val data: T 13 | ) -------------------------------------------------------------------------------- /Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/mfa/MfaStatus.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.auth.mfa 2 | 3 | /** 4 | * Represents the MFA status of a user. 5 | * @property enabled Whether MFA is enabled for the user. If true, the user can log in using MFA and has at least one verified factor. 6 | * @property active Whether MFA is active for the user. If true, the user is logged in using MFA. 7 | */ 8 | data class MfaStatus( 9 | val enabled: Boolean, 10 | val active: Boolean 11 | ) 12 | -------------------------------------------------------------------------------- /Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/providers/ExternalAuthConfig.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.auth.providers 2 | 3 | import io.github.jan.supabase.auth.Auth 4 | 5 | /** 6 | * Configuration for external authentication providers like Google, Twitter, etc. 7 | */ 8 | expect class ExternalAuthConfig(): ExternalAuthConfigDefaults 9 | 10 | /** 11 | * The default values for [ExternalAuthConfig] 12 | */ 13 | open class ExternalAuthConfigDefaults { 14 | 15 | /** 16 | * The scopes to request from the external provider 17 | */ 18 | val scopes = mutableListOf() 19 | 20 | /** 21 | * Additional query parameters to send to the external provider 22 | */ 23 | val queryParams = mutableMapOf() 24 | 25 | /** 26 | * Automatically open the URL in the browser. Only applies to [Auth.linkIdentity]. 27 | */ 28 | var automaticallyOpenUrl: Boolean = true 29 | 30 | } -------------------------------------------------------------------------------- /Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/status/RefreshFailureCause.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.auth.status 2 | 3 | import io.github.jan.supabase.exceptions.RestException 4 | 5 | /** 6 | * Represents the cause of a refresh error 7 | */ 8 | //Note: Move this interface when removing the deprecated property from [SessionStatus.RefreshFailure] 9 | sealed interface RefreshFailureCause { 10 | 11 | /** 12 | * The refresh failed due to a network error 13 | * @param exception The exception that caused the error 14 | */ 15 | data class NetworkError(val exception: Throwable) : RefreshFailureCause 16 | 17 | /** 18 | * The refresh failed due to an internal server error 19 | * @param exception The rest exception that caused the error 20 | */ 21 | data class InternalServerError(val exception: RestException) : RefreshFailureCause 22 | 23 | } -------------------------------------------------------------------------------- /Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/user/Identity.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("UndocumentedPublicClass", "UndocumentedPublicFunction", "UndocumentedPublicProperty") 2 | package io.github.jan.supabase.auth.user 3 | 4 | 5 | import kotlinx.serialization.SerialName 6 | import kotlinx.serialization.Serializable 7 | import kotlinx.serialization.json.JsonObject 8 | 9 | @Serializable 10 | data class Identity( 11 | @SerialName("id") 12 | val id: String, 13 | @SerialName("identity_data") 14 | val identityData: JsonObject, 15 | @SerialName("identity_id") 16 | val identityId: String? = null, 17 | @SerialName("last_sign_in_at") 18 | val lastSignInAt: String? = null, 19 | @SerialName("updated_at") 20 | val updatedAt: String? = null, 21 | @SerialName("created_at") 22 | val createdAt: String? = null, 23 | @SerialName("provider") 24 | val provider: String, 25 | @SerialName("user_id") 26 | val userId: String 27 | ) -------------------------------------------------------------------------------- /Auth/src/commonTest/kotlin/AuthTestUtils.kt: -------------------------------------------------------------------------------- 1 | import io.github.jan.supabase.auth.Auth 2 | import io.github.jan.supabase.auth.status.SessionStatus 3 | 4 | fun Auth.sessionSource() = (sessionStatus.value as SessionStatus.Authenticated).source 5 | 6 | inline fun Auth.sessionSourceAs() = sessionSource() as T -------------------------------------------------------------------------------- /Auth/src/commonTest/kotlin/CodeVerifierCacheTest.kt: -------------------------------------------------------------------------------- 1 | import io.github.jan.supabase.auth.MemoryCodeVerifierCache 2 | import kotlinx.coroutines.test.runTest 3 | import kotlin.test.Test 4 | import kotlin.test.assertEquals 5 | import kotlin.test.assertNull 6 | 7 | class CodeVerifierCacheTest { 8 | 9 | @Test 10 | fun testMemoryCodeVerifierCache() { 11 | runTest { 12 | val codeVerifier = "codeVerifier" 13 | val codeVerifierCache = MemoryCodeVerifierCache(codeVerifier) 14 | assertEquals(codeVerifier, codeVerifierCache.loadCodeVerifier()) 15 | val newCodeVerifier = "newCodeVerifier" 16 | codeVerifierCache.saveCodeVerifier(newCodeVerifier) 17 | assertEquals(newCodeVerifier, codeVerifierCache.loadCodeVerifier()) 18 | codeVerifierCache.deleteCodeVerifier() 19 | assertNull(codeVerifierCache.loadCodeVerifier()) 20 | } 21 | } 22 | 23 | } -------------------------------------------------------------------------------- /Auth/src/desktopMain/kotlin/io/github/jan/supabase/auth/Utils.desktop.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.auth 2 | 3 | import io.github.jan.supabase.auth.server.createServer 4 | import io.github.jan.supabase.auth.user.UserSession 5 | import kotlinx.coroutines.Dispatchers 6 | import kotlinx.coroutines.IO 7 | import kotlinx.coroutines.withContext 8 | 9 | internal actual suspend fun Auth.startExternalAuth( 10 | redirectUrl: String?, 11 | getUrl: suspend (redirectTo: String?) -> String, 12 | onSessionSuccess: suspend (UserSession) -> Unit 13 | ) { 14 | withContext(Dispatchers.IO) { 15 | if(redirectUrl != null) { 16 | config.urlLauncher.openUrl(supabaseClient, getUrl(redirectUrl)) 17 | return@withContext 18 | } 19 | createServer({ 20 | getUrl(it) 21 | }, supabaseClient.auth, onSessionSuccess) 22 | } 23 | } -------------------------------------------------------------------------------- /Auth/src/iosMain/kotlin/io/github/jan/supabase/auth/AuthConfig.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.auth 2 | 3 | import io.github.jan.supabase.plugins.CustomSerializationConfig 4 | 5 | /** 6 | * The configuration for [Auth] 7 | */ 8 | actual class AuthConfig : CustomSerializationConfig, AuthConfigDefaults() -------------------------------------------------------------------------------- /Auth/src/iosMain/kotlin/io/github/jan/supabase/auth/providers/openUrl.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.auth.providers 2 | 3 | import io.github.jan.supabase.auth.Auth 4 | import io.github.jan.supabase.logging.d 5 | import io.github.jan.supabase.logging.e 6 | import platform.Foundation.NSURL 7 | import platform.UIKit.UIApplication 8 | 9 | internal actual fun openUrl(url: NSURL) { 10 | UIApplication.sharedApplication.openURL(url, emptyMap()) { 11 | if(it) Auth.logger.d { "Successfully opened provider url in safari" } else Auth.logger.e { "Failed to open provider url in safari" } 12 | } 13 | } -------------------------------------------------------------------------------- /Auth/src/jsMain/kotlin/io/github/jan/supabase/auth/AuthConfig.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.auth 2 | 3 | import io.github.jan.supabase.plugins.CustomSerializationConfig 4 | 5 | /** 6 | * The configuration for [Auth] 7 | */ 8 | actual class AuthConfig : CustomSerializationConfig, AuthConfigDefaults() -------------------------------------------------------------------------------- /Auth/src/jsMain/kotlin/io/github/jan/supabase/auth/RedirectUrl.js.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.auth 2 | 3 | import io.github.jan.supabase.annotations.SupabaseInternal 4 | import kotlinx.browser.window 5 | 6 | @SupabaseInternal 7 | internal actual fun Auth.defaultPlatformRedirectUrl(): String? = window.location.origin -------------------------------------------------------------------------------- /Auth/src/jsMain/kotlin/io/github/jan/supabase/auth/Utils.js.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.auth 2 | 3 | import io.github.jan.supabase.SupabaseClient 4 | import kotlinx.browser.window 5 | 6 | internal actual suspend fun SupabaseClient.openExternalUrl(url: String) { 7 | window.location.href = url 8 | } -------------------------------------------------------------------------------- /Auth/src/jsMain/kotlin/io/github/jan/supabase/auth/providers/ExternalAuthConfig.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.auth.providers 2 | 3 | /** 4 | * Configuration for external authentication providers like Google, Twitter, etc. 5 | */ 6 | actual class ExternalAuthConfig: ExternalAuthConfigDefaults() -------------------------------------------------------------------------------- /Auth/src/jsTest/kotlin/platformSettings.kt: -------------------------------------------------------------------------------- 1 | import io.github.jan.supabase.auth.AuthConfig 2 | 3 | actual fun AuthConfig.platformSettings() = Unit -------------------------------------------------------------------------------- /Auth/src/jvmMain/kotlin/io/github/jan/supabase/auth/RedirectUrl.jvm.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.auth 2 | 3 | import io.github.jan.supabase.annotations.SupabaseInternal 4 | 5 | @SupabaseInternal 6 | internal actual fun Auth.defaultPlatformRedirectUrl(): String? = null -------------------------------------------------------------------------------- /Auth/src/jvmMain/kotlin/io/github/jan/supabase/auth/Utils.jvm.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.auth 2 | 3 | import io.github.jan.supabase.SupabaseClient 4 | import kotlinx.coroutines.Dispatchers 5 | import kotlinx.coroutines.withContext 6 | import java.awt.Desktop 7 | import java.net.URI 8 | 9 | internal actual suspend fun SupabaseClient.openExternalUrl(url: String) { 10 | withContext(Dispatchers.IO) { 11 | Desktop.getDesktop() 12 | .browse(URI(url)) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Auth/src/jvmMain/kotlin/io/github/jan/supabase/auth/providers/ExternalAuthConfig.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.auth.providers 2 | 3 | /** 4 | * Configuration for external authentication providers like Google, Twitter, etc. 5 | */ 6 | actual class ExternalAuthConfig: ExternalAuthConfigDefaults() -------------------------------------------------------------------------------- /Auth/src/jvmMain/kotlin/io/github/jan/supabase/auth/setupPlatform.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.auth 2 | 3 | import io.github.jan.supabase.annotations.SupabaseInternal 4 | 5 | @SupabaseInternal 6 | actual fun Auth.setupPlatform() = Unit -------------------------------------------------------------------------------- /Auth/src/jvmTest/kotlin/platformSettings.kt: -------------------------------------------------------------------------------- 1 | import io.github.jan.supabase.auth.AuthConfig 2 | 3 | actual fun AuthConfig.platformSettings() = Unit -------------------------------------------------------------------------------- /Auth/src/linuxMain/kotlin/io/github/jan/supabase/auth/SettingsUtil.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.auth 2 | 3 | import io.github.jan.supabase.annotations.SupabaseInternal 4 | 5 | @SupabaseInternal 6 | actual fun Auth.createDefaultSessionManager(): SessionManager = MemorySessionManager() 7 | 8 | @SupabaseInternal 9 | actual fun Auth.createDefaultCodeVerifierCache(): CodeVerifierCache = MemoryCodeVerifierCache() -------------------------------------------------------------------------------- /Auth/src/linuxMain/kotlin/io/github/jan/supabase/auth/Utils.linux.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.auth 2 | 3 | import io.github.jan.supabase.SupabaseClient 4 | import platform.posix.system 5 | 6 | internal actual suspend fun SupabaseClient.openExternalUrl(url: String) { 7 | system("xdg-open $url") 8 | } -------------------------------------------------------------------------------- /Auth/src/linuxMain/kotlin/io/github/jan/supabase/auth/generateRedirectUrl.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.auth 2 | 3 | import io.github.jan.supabase.annotations.SupabaseInternal 4 | 5 | @SupabaseInternal 6 | internal actual fun Auth.defaultPlatformRedirectUrl(): String? = null -------------------------------------------------------------------------------- /Auth/src/linuxMain/kotlin/io/github/jan/supabase/auth/providers/ExternalAuthConfig.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.auth.providers 2 | 3 | /** 4 | * Configuration for external authentication providers like Google, Twitter, etc. 5 | */ 6 | actual class ExternalAuthConfig: ExternalAuthConfigDefaults() -------------------------------------------------------------------------------- /Auth/src/linuxMain/kotlin/io/github/jan/supabase/auth/setupPlatform.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.auth 2 | 3 | import io.github.jan.supabase.annotations.SupabaseInternal 4 | import io.github.jan.supabase.logging.w 5 | 6 | @SupabaseInternal 7 | actual fun Auth.setupPlatform() { 8 | Auth.logger.w { "Linux support is experimental, please report any bugs you find!" } 9 | } -------------------------------------------------------------------------------- /Auth/src/linuxTest/kotlin/platformSettings.kt: -------------------------------------------------------------------------------- 1 | import io.github.jan.supabase.auth.AuthConfig 2 | 3 | actual fun AuthConfig.platformSettings() = Unit -------------------------------------------------------------------------------- /Auth/src/macosMain/kotlin/io/github/jan/supabase/auth/providers/openUrl.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.auth.providers 2 | 3 | import io.github.jan.supabase.auth.Auth 4 | import io.github.jan.supabase.logging.d 5 | import io.github.jan.supabase.logging.e 6 | import platform.AppKit.NSWorkspace 7 | import platform.AppKit.NSWorkspaceOpenConfiguration 8 | import platform.Foundation.NSURL 9 | 10 | internal actual fun openUrl(url: NSURL) { 11 | NSWorkspace.sharedWorkspace().openURL(url, NSWorkspaceOpenConfiguration()) { _, error -> 12 | if(error != null) Auth.logger.d { "Successfully opened provider url in safari" } else Auth.logger.e { "Failed to open provider url in safari" } 13 | } 14 | } -------------------------------------------------------------------------------- /Auth/src/mingwMain/kotlin/io/github/jan/supabase/auth/Utils.mingw.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.auth 2 | 3 | import io.github.jan.supabase.SupabaseClient 4 | import kotlinx.cinterop.ExperimentalForeignApi 5 | import platform.windows.SW_SHOWNORMAL 6 | import platform.windows.ShellExecuteW 7 | 8 | @OptIn(ExperimentalForeignApi::class) 9 | internal actual suspend fun SupabaseClient.openExternalUrl(url: String) { 10 | ShellExecuteW(null, "open", url, null, null, SW_SHOWNORMAL); 11 | } -------------------------------------------------------------------------------- /Auth/src/mingwMain/kotlin/io/github/jan/supabase/auth/generateRedirectUrl.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.auth 2 | 3 | import io.github.jan.supabase.annotations.SupabaseInternal 4 | 5 | @SupabaseInternal 6 | internal actual fun Auth.defaultPlatformRedirectUrl(): String? = null -------------------------------------------------------------------------------- /Auth/src/mingwMain/kotlin/io/github/jan/supabase/auth/providers/ExternalAuthConfig.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.auth.providers 2 | 3 | /** 4 | * Configuration for external authentication providers like Google, Twitter, etc. 5 | */ 6 | actual class ExternalAuthConfig : 7 | ExternalAuthConfigDefaults() -------------------------------------------------------------------------------- /Auth/src/mingwMain/kotlin/io/github/jan/supabase/auth/setupPlatform.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.auth 2 | 3 | import io.github.jan.supabase.annotations.SupabaseInternal 4 | import io.github.jan.supabase.logging.w 5 | 6 | @SupabaseInternal 7 | actual fun Auth.setupPlatform() { 8 | Auth.logger.w { "Windows support is experimental, please report any bugs you find!" } 9 | } -------------------------------------------------------------------------------- /Auth/src/mingwTest/kotlin/platformSettings.kt: -------------------------------------------------------------------------------- 1 | import io.github.jan.supabase.auth.AuthConfig 2 | 3 | actual fun AuthConfig.platformSettings() = Unit -------------------------------------------------------------------------------- /Auth/src/nonDesktopMain/kotlin/io/github/jan/supabase/auth/Utils.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("RedundantSuspendModifier") 2 | package io.github.jan.supabase.auth 3 | 4 | import io.github.jan.supabase.auth.user.UserSession 5 | 6 | internal actual suspend fun Auth.startExternalAuth( 7 | redirectUrl: String?, 8 | getUrl: suspend (redirectTo: String?) -> String, 9 | onSessionSuccess: suspend (UserSession) -> Unit 10 | ) { 11 | config.urlLauncher.openUrl(supabaseClient, getUrl(redirectUrl)) 12 | } -------------------------------------------------------------------------------- /Auth/src/settingsTest/kotlin/SettingsTest.kt: -------------------------------------------------------------------------------- 1 | import io.github.jan.supabase.auth.createDefaultSettingsKey 2 | import kotlin.test.Test 3 | import kotlin.test.assertEquals 4 | 5 | class SettingsTest { 6 | 7 | @Test 8 | fun testSettingsKey() { 9 | val supabaseUrl = "id.supabase.co" 10 | val key = createDefaultSettingsKey(supabaseUrl) 11 | assertEquals("sb-id-supabase-co", key) 12 | } 13 | 14 | } -------------------------------------------------------------------------------- /Auth/src/tvosMain/kotlin/io/github/jan/supabase/auth/AuthConfig.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.auth 2 | 3 | import io.github.jan.supabase.plugins.CustomSerializationConfig 4 | 5 | /** 6 | * The configuration for [Auth] 7 | */ 8 | actual class AuthConfig : CustomSerializationConfig, AuthConfigDefaults() -------------------------------------------------------------------------------- /Auth/src/tvosMain/kotlin/io/github/jan/supabase/auth/providers/openUrl.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.auth.providers 2 | 3 | import platform.Foundation.NSURL 4 | 5 | internal actual fun openUrl(url: NSURL) { 6 | error("Can't open url on tvOS") 7 | } -------------------------------------------------------------------------------- /Auth/src/wasmJsMain/kotlin/io/github/jan/supabase/auth/AuthConfig.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.auth 2 | 3 | import io.github.jan.supabase.plugins.CustomSerializationConfig 4 | 5 | /** 6 | * The configuration for [Auth] 7 | */ 8 | actual class AuthConfig: CustomSerializationConfig, AuthConfigDefaults() -------------------------------------------------------------------------------- /Auth/src/wasmJsMain/kotlin/io/github/jan/supabase/auth/RedirectUrl.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.auth 2 | 3 | import io.github.jan.supabase.annotations.SupabaseInternal 4 | import kotlinx.browser.window 5 | 6 | @SupabaseInternal 7 | actual fun Auth.defaultPlatformRedirectUrl(): String? = window.location.origin -------------------------------------------------------------------------------- /Auth/src/wasmJsMain/kotlin/io/github/jan/supabase/auth/Utils.wasmJs.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.auth 2 | 3 | import io.github.jan.supabase.SupabaseClient 4 | import kotlinx.browser.window 5 | 6 | internal actual suspend fun SupabaseClient.openExternalUrl(url: String) { 7 | window.location.href = url 8 | } -------------------------------------------------------------------------------- /Auth/src/wasmJsMain/kotlin/io/github/jan/supabase/auth/providers/ExternalAuthConfig.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.auth.providers 2 | 3 | /** 4 | * Configuration for external authentication providers like Google, Twitter, etc. 5 | */ 6 | actual class ExternalAuthConfig: ExternalAuthConfigDefaults() -------------------------------------------------------------------------------- /Auth/src/wasmJsTest/kotlin/platformSettings.kt: -------------------------------------------------------------------------------- 1 | import io.github.jan.supabase.auth.AuthConfig 2 | 3 | actual fun AuthConfig.platformSettings() = Unit -------------------------------------------------------------------------------- /Auth/src/watchosMain/kotlin/io/github/jan/supabase/auth/AuthConfig.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.auth 2 | 3 | import io.github.jan.supabase.plugins.CustomSerializationConfig 4 | 5 | /** 6 | * The configuration for [Auth] 7 | */ 8 | actual class AuthConfig : CustomSerializationConfig, AuthConfigDefaults() -------------------------------------------------------------------------------- /Auth/src/watchosMain/kotlin/io/github/jan/supabase/auth/providers/openUrl.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.auth.providers 2 | 3 | import platform.Foundation.NSURL 4 | 5 | internal actual fun openUrl(url: NSURL) { 6 | error("Can't open url on watchOS") 7 | } -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @jan-tennert 2 | -------------------------------------------------------------------------------- /Functions/src/androidMain/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /Functions/src/commonMain/kotlin/io/github/jan/supabase/functions/FunctionRegion.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("UndocumentedPublicProperty") 2 | package io.github.jan.supabase.functions 3 | 4 | /** 5 | * The region where the function is invoked. 6 | * @param value The value of the region 7 | */ 8 | enum class FunctionRegion(val value: String) { 9 | ANY("any"), 10 | AP_NORTHEAST_1("ap-northeast-1"), 11 | AP_NORTHEAST_2("ap-northeast-2"), 12 | AP_SOUTH_1("ap-south-1"), 13 | AP_SOUTHEAST_1("ap-southeast-1"), 14 | AP_SOUTHEAST_2("ap-southeast-2"), 15 | CA_CENTRAL_1("ca-central-1"), 16 | EU_CENTRAL_1("eu-central-1"), 17 | EU_WEST_1("eu-west-1"), 18 | EU_WEST_2("eu-west-2"), 19 | EU_WEST_3("eu-west-3"), 20 | SA_EAST_1("sa-east-1"), 21 | US_EAST_1("us-east-1"), 22 | US_WEST_1("us-west-1"), 23 | US_WEST_2("us-west-2"), 24 | } -------------------------------------------------------------------------------- /Postgrest/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id(libs.plugins.kotlin.multiplatform.get().pluginId) 3 | id(libs.plugins.android.library.get().pluginId) 4 | } 5 | 6 | description = "Extends supabase-kt with a Postgrest Client" 7 | 8 | repositories { 9 | mavenCentral() 10 | } 11 | 12 | @OptIn(org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi::class) 13 | kotlin { 14 | defaultConfig() 15 | allTargets() 16 | sourceSets { 17 | val commonMain by getting { 18 | dependencies { 19 | addModules(SupabaseModule.AUTH) 20 | api(libs.kotlin.reflect) 21 | } 22 | } 23 | val commonTest by getting { 24 | dependencies { 25 | implementation(libs.bundles.testing) 26 | implementation(project(":test-common")) 27 | } 28 | } 29 | } 30 | } 31 | 32 | configureLibraryAndroidTarget() -------------------------------------------------------------------------------- /Postgrest/src/androidMain/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Postgrest/src/androidMain/kotlin/io/github/jan/supabase/postgrest/getColumnName.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.postgrest 2 | 3 | import io.github.jan.supabase.annotations.SupabaseInternal 4 | import kotlinx.serialization.SerialName 5 | import kotlin.reflect.KProperty1 6 | import kotlin.reflect.full.findAnnotation 7 | 8 | @SupabaseInternal 9 | actual fun getSerialName(property: KProperty1): String { 10 | val serialName = property.findAnnotation() 11 | return serialName?.value ?: property.name 12 | } 13 | -------------------------------------------------------------------------------- /Postgrest/src/appleMain/kotlin/io/github/jan/supabase/postgrest/getColumnName.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.postgrest 2 | 3 | import io.github.jan.supabase.annotations.SupabaseInternal 4 | import kotlin.reflect.KProperty1 5 | 6 | @SupabaseInternal 7 | actual fun getSerialName(property: KProperty1) = property.name -------------------------------------------------------------------------------- /Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/PostgrestDsl.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.postgrest 2 | 3 | import io.github.jan.supabase.annotations.SupabaseInternal 4 | 5 | @DslMarker 6 | @Target(AnnotationTarget.CLASS, AnnotationTarget.TYPE) 7 | @SupabaseInternal 8 | annotation class PostgrestDsl 9 | -------------------------------------------------------------------------------- /Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/PostgrestErrorResponse.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.postgrest 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | @Serializable 6 | internal data class PostgrestErrorResponse( 7 | val message: String, 8 | val hint: String? = null, 9 | val details: String? = null, 10 | val code: String? = null, 11 | ) 12 | -------------------------------------------------------------------------------- /Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/exception/PostgrestRestException.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.postgrest.exception 2 | 3 | import io.github.jan.supabase.exceptions.RestException 4 | import io.ktor.client.statement.HttpResponse 5 | 6 | /** 7 | * Exception thrown when a Postgrest request fails 8 | * @param message The error message 9 | * @param hint A hint to the error 10 | * @param details Additional details about the error 11 | * @param code The error code 12 | * @param response The response that caused the exception 13 | */ 14 | class PostgrestRestException( 15 | message: String, 16 | val hint: String?, 17 | val details: String?, 18 | val code: String?, 19 | response: HttpResponse 20 | ): RestException(message, hint ?: details, response) -------------------------------------------------------------------------------- /Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/executor/RequestExecutor.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.postgrest.executor 2 | 3 | import io.github.jan.supabase.postgrest.Postgrest 4 | import io.github.jan.supabase.postgrest.request.PostgrestRequest 5 | import io.github.jan.supabase.postgrest.result.PostgrestResult 6 | 7 | /** 8 | * Request executor interface 9 | * 10 | */ 11 | sealed interface RequestExecutor { 12 | 13 | /** 14 | * Execute given PostgrestRequest 15 | * 16 | * @param path [String] 17 | * @param request [PostgrestRequest] 18 | * @return [PostgrestResult] 19 | */ 20 | suspend fun execute(postgrest: Postgrest, path: String, request: PostgrestRequest): PostgrestResult 21 | } -------------------------------------------------------------------------------- /Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/executor/RestRequestExecutor.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.postgrest.executor 2 | 3 | import io.github.jan.supabase.postgrest.Postgrest 4 | import io.github.jan.supabase.postgrest.PostgrestImpl 5 | import io.github.jan.supabase.postgrest.request.PostgrestRequest 6 | import io.github.jan.supabase.postgrest.result.PostgrestResult 7 | 8 | @PublishedApi 9 | internal data object RestRequestExecutor : RequestExecutor { 10 | 11 | override suspend fun execute( 12 | postgrest: Postgrest, 13 | path: String, 14 | request: PostgrestRequest 15 | ): PostgrestResult { 16 | val authenticatedSupabaseApi = (postgrest as PostgrestImpl).api 17 | return authenticatedSupabaseApi.request(path) { 18 | configurePostgrestRequest(request) 19 | }.asPostgrestResult(postgrest) 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/query/Order.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.postgrest.query 2 | 3 | /** 4 | * Used to order the result of a query 5 | * @param value The value to be used in the query 6 | */ 7 | enum class Order(val value: String) { 8 | /** 9 | * Order the data ascending 10 | */ 11 | ASCENDING("asc"), 12 | 13 | /** 14 | * Order the data descending 15 | */ 16 | DESCENDING("desc"); 17 | } 18 | -------------------------------------------------------------------------------- /Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/query/filter/FilterOperation.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress( 2 | "UndocumentedPublicClass", 3 | "UndocumentedPublicFunction", 4 | "UndocumentedPublicProperty" 5 | ) 6 | package io.github.jan.supabase.postgrest.query.filter 7 | 8 | /** 9 | * Represents a filter operation for a column using a specific operator and a value. 10 | */ 11 | data class FilterOperation(val column: String, val operator: FilterOperator, val value: Any) 12 | -------------------------------------------------------------------------------- /Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/query/filter/FilterOperator.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress( 2 | "UndocumentedPublicClass", 3 | "UndocumentedPublicFunction", 4 | "UndocumentedPublicProperty" 5 | ) 6 | package io.github.jan.supabase.postgrest.query.filter 7 | 8 | /** 9 | * Represents a single filter operation 10 | */ 11 | enum class FilterOperator(val identifier: String) { 12 | EQ("eq"), 13 | NEQ("neq"), 14 | GT("gt"), 15 | GTE("gte"), 16 | LT("lt"), 17 | LTE("lte"), 18 | LIKE("like"), 19 | MATCH("match"), 20 | ILIKE("ilike"), 21 | IMATCH("imatch"), 22 | IS("is"), 23 | IN("in"), 24 | CS("cs"), 25 | CD("cd"), 26 | SL("sl"), 27 | SR("sr"), 28 | NXL("nxl"), 29 | NXR("nxr"), 30 | ADJ("adj"), 31 | OV("ov"), 32 | FTS("fts"), 33 | PLFTS("plfts"), 34 | PHFTS("phfts"), 35 | WFTS("wfts"), 36 | } -------------------------------------------------------------------------------- /Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/query/filter/TextSearchType.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress( 2 | "UndocumentedPublicClass", 3 | "UndocumentedPublicFunction", 4 | "UndocumentedPublicProperty" 5 | ) 6 | package io.github.jan.supabase.postgrest.query.filter 7 | 8 | /** 9 | * Used to search rows using a full text search. See [Postgrest](https://postgrest.org/en/stable/api.html#full-text-search) for more information 10 | */ 11 | enum class TextSearchType(val identifier: String) { 12 | NONE(""), 13 | PLAINTO("pl"), 14 | PHRASETO("ph"), 15 | WEBSEARCH("w") 16 | } -------------------------------------------------------------------------------- /Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/query/request/InsertRequestBuilder.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.postgrest.query.request 2 | 3 | import io.github.jan.supabase.postgrest.PropertyConversionMethod 4 | import io.github.jan.supabase.postgrest.query.PostgrestQueryBuilder 5 | import io.github.jan.supabase.postgrest.query.PostgrestRequestBuilder 6 | 7 | /** 8 | * Request builder for [PostgrestQueryBuilder.insert] 9 | */ 10 | open class InsertRequestBuilder(propertyConversionMethod: PropertyConversionMethod): PostgrestRequestBuilder(propertyConversionMethod) { 11 | 12 | /** 13 | * Make missing fields default to `null`. 14 | * Otherwise, use the default value for the column. This only applies when 15 | * inserting new rows, not when merging with existing rows under 16 | */ 17 | var defaultToNull: Boolean = true 18 | 19 | } -------------------------------------------------------------------------------- /Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/query/request/RpcRequestBuilder.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.postgrest.query.request 2 | 3 | import io.github.jan.supabase.postgrest.Postgrest 4 | import io.github.jan.supabase.postgrest.PropertyConversionMethod 5 | import io.github.jan.supabase.postgrest.RpcMethod 6 | import io.github.jan.supabase.postgrest.query.PostgrestRequestBuilder 7 | 8 | /** 9 | * Request builder for [Postgrest.rpc] 10 | */ 11 | class RpcRequestBuilder(defaultSchema: String, propertyConversionMethod: PropertyConversionMethod): PostgrestRequestBuilder(propertyConversionMethod) { 12 | 13 | /** 14 | * The HTTP method to use. Default is POST 15 | */ 16 | var method: RpcMethod = RpcMethod.POST 17 | 18 | /** 19 | * The database schema 20 | */ 21 | var schema: String = defaultSchema 22 | 23 | } -------------------------------------------------------------------------------- /Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/query/request/SelectRequestBuilder.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.postgrest.query.request 2 | 3 | import io.github.jan.supabase.postgrest.PropertyConversionMethod 4 | import io.github.jan.supabase.postgrest.query.PostgrestQueryBuilder 5 | import io.github.jan.supabase.postgrest.query.PostgrestRequestBuilder 6 | 7 | /** 8 | * Request builder for [PostgrestQueryBuilder.select] 9 | */ 10 | class SelectRequestBuilder(propertyConversionMethod: PropertyConversionMethod): PostgrestRequestBuilder(propertyConversionMethod) { 11 | 12 | /** 13 | * If true, no body will be returned. Useful when using count. 14 | */ 15 | var head: Boolean = false 16 | 17 | } -------------------------------------------------------------------------------- /Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/query/request/UpsertRequestBuilder.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.postgrest.query.request 2 | 3 | import io.github.jan.supabase.postgrest.PropertyConversionMethod 4 | import io.github.jan.supabase.postgrest.query.PostgrestQueryBuilder 5 | 6 | /** 7 | * Request builder for [PostgrestQueryBuilder.upsert] 8 | */ 9 | class UpsertRequestBuilder(propertyConversionMethod: PropertyConversionMethod): InsertRequestBuilder(propertyConversionMethod) { 10 | 11 | /** 12 | * Comma-separated UNIQUE column(s) to specify how 13 | * duplicate rows are determined. Two rows are duplicates if all the 14 | * `onConflict` columns are equal. 15 | */ 16 | var onConflict: String? = null 17 | 18 | /** 19 | * If `true`, duplicate rows are ignored. If `false`, duplicate rows are merged with existing rows. 20 | */ 21 | var ignoreDuplicates: Boolean = false 22 | 23 | } -------------------------------------------------------------------------------- /Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/request/DeleteRequest.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.postgrest.request 2 | 3 | import io.github.jan.supabase.postgrest.query.Count 4 | import io.github.jan.supabase.postgrest.query.Returning 5 | import io.ktor.http.Headers 6 | import io.ktor.http.HttpMethod 7 | 8 | @PublishedApi 9 | internal class DeleteRequest( 10 | override val returning: Returning = Returning.Minimal, 11 | private val count: Count? = null, 12 | override val urlParams: Map, 13 | override val schema: String, 14 | override val headers: Headers = Headers.Empty, 15 | ) : PostgrestRequest { 16 | 17 | override val method = HttpMethod.Delete 18 | override val prefer = buildList { 19 | add("return=${returning.identifier}") 20 | if (count != null) add("count=${count.identifier}") 21 | } 22 | 23 | } -------------------------------------------------------------------------------- /Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/request/PostgrestRequest.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress( 2 | "UndocumentedPublicClass", 3 | "UndocumentedPublicFunction", 4 | "UndocumentedPublicProperty" 5 | ) 6 | 7 | package io.github.jan.supabase.postgrest.request 8 | 9 | import io.github.jan.supabase.annotations.SupabaseInternal 10 | import io.github.jan.supabase.postgrest.query.Returning 11 | import io.ktor.http.Headers 12 | import io.ktor.http.HttpMethod 13 | import kotlinx.serialization.json.JsonElement 14 | 15 | @SupabaseInternal 16 | sealed interface PostgrestRequest { 17 | 18 | val body: JsonElement? get() = null 19 | val method: HttpMethod 20 | val urlParams: Map 21 | val headers: Headers get() = Headers.Empty 22 | val returning: Returning get() = Returning.Minimal 23 | val prefer: List 24 | val schema: String 25 | 26 | } -------------------------------------------------------------------------------- /Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/request/RpcRequest.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.postgrest.request 2 | 3 | import io.github.jan.supabase.postgrest.query.Count 4 | import io.ktor.http.Headers 5 | import io.ktor.http.HttpMethod 6 | import kotlinx.serialization.json.JsonElement 7 | 8 | @PublishedApi 9 | internal class RpcRequest( 10 | override val method: HttpMethod, 11 | val count: Count? = null, 12 | override val urlParams: Map, 13 | override val body: JsonElement? = null, 14 | override val schema: String = "public", 15 | override val headers: Headers = Headers.Empty 16 | ) : PostgrestRequest { 17 | 18 | override val prefer = if (count != null) listOf("count=${count.identifier}") else listOf() 19 | 20 | } -------------------------------------------------------------------------------- /Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/request/SelectRequest.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.postgrest.request 2 | 3 | import io.github.jan.supabase.postgrest.query.Count 4 | import io.ktor.http.Headers 5 | import io.ktor.http.HttpMethod 6 | 7 | @PublishedApi 8 | internal class SelectRequest( 9 | val head: Boolean = false, 10 | val count: Count? = null, 11 | override val urlParams: Map, 12 | override val schema: String, 13 | override val headers: Headers = Headers.Empty, 14 | ): PostgrestRequest { 15 | 16 | override val method = if (head) HttpMethod.Head else HttpMethod.Get 17 | override val prefer = if (count != null) listOf("count=${count.identifier}") else listOf() 18 | 19 | } -------------------------------------------------------------------------------- /Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/request/UpdateRequest.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.postgrest.request 2 | 3 | import io.github.jan.supabase.postgrest.query.Count 4 | import io.github.jan.supabase.postgrest.query.Returning 5 | import io.ktor.http.Headers 6 | import io.ktor.http.HttpMethod 7 | import kotlinx.serialization.json.JsonElement 8 | 9 | @PublishedApi 10 | internal class UpdateRequest( 11 | override val returning: Returning = Returning.Minimal, 12 | private val count: Count? = null, 13 | override val urlParams: Map, 14 | override val body: JsonElement, 15 | override val schema: String, 16 | override val headers: Headers = Headers.Empty, 17 | ) : PostgrestRequest { 18 | 19 | override val method = HttpMethod.Patch 20 | override val prefer = buildList { 21 | add("return=${returning.identifier}") 22 | if (count != null) add("count=${count.identifier}") 23 | } 24 | 25 | } -------------------------------------------------------------------------------- /Postgrest/src/commonTest/kotlin/Utils.kt: -------------------------------------------------------------------------------- 1 | import io.github.jan.supabase.annotations.SupabaseInternal 2 | import io.github.jan.supabase.postgrest.PropertyConversionMethod 3 | import io.github.jan.supabase.postgrest.query.PostgrestRequestBuilder 4 | 5 | @SupabaseInternal 6 | inline fun postgrestRequest(propertyConversionMethod: PropertyConversionMethod = PropertyConversionMethod.CAMEL_CASE_TO_SNAKE_CASE, block: PostgrestRequestBuilder.() -> Unit): PostgrestRequestBuilder { 7 | val filter = PostgrestRequestBuilder(propertyConversionMethod) 8 | filter.block() 9 | return filter 10 | } -------------------------------------------------------------------------------- /Postgrest/src/jsMain/kotlin/io/github/jan/supabase/postgrest/getColumnName.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.postgrest 2 | 3 | import io.github.jan.supabase.annotations.SupabaseInternal 4 | import kotlin.reflect.KProperty1 5 | 6 | @SupabaseInternal 7 | actual fun getSerialName(property: KProperty1) = property.name 8 | -------------------------------------------------------------------------------- /Postgrest/src/jvmMain/kotlin/io/github/jan/supabase/postgrest/getColumnName.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.postgrest 2 | 3 | import io.github.jan.supabase.annotations.SupabaseInternal 4 | import kotlinx.serialization.SerialName 5 | import kotlin.reflect.KProperty1 6 | import kotlin.reflect.full.findAnnotation 7 | 8 | @SupabaseInternal 9 | actual fun getSerialName(property: KProperty1): String { 10 | val serialName = property.findAnnotation() 11 | return serialName?.value ?: property.name 12 | } 13 | -------------------------------------------------------------------------------- /Postgrest/src/linuxX64Main/kotlin/io/github/jan/supabase/postgrest/getSerialName.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.postgrest 2 | 3 | import io.github.jan.supabase.annotations.SupabaseInternal 4 | import kotlin.reflect.KProperty1 5 | 6 | @SupabaseInternal 7 | actual fun getSerialName(property: KProperty1): String = property.name -------------------------------------------------------------------------------- /Postgrest/src/mingwX64Main/kotlin/io/github/jan/supabase/postgrest/getSerialName.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.postgrest 2 | 3 | import io.github.jan.supabase.annotations.SupabaseInternal 4 | import kotlin.reflect.KProperty1 5 | 6 | @SupabaseInternal 7 | actual fun getSerialName(property: KProperty1): String = property.name -------------------------------------------------------------------------------- /Postgrest/src/wasmJsMain/kotlin/io/github/jan/supabase/postgrest/getColumnName.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.postgrest 2 | 3 | import io.github.jan.supabase.annotations.SupabaseInternal 4 | import kotlin.reflect.KProperty1 5 | 6 | @SupabaseInternal 7 | actual fun getSerialName(property: KProperty1) = property.name 8 | -------------------------------------------------------------------------------- /Realtime/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id(libs.plugins.kotlin.multiplatform.get().pluginId) 3 | id(libs.plugins.android.library.get().pluginId) 4 | } 5 | 6 | description = "Extends supabase-kt with a Realtime Client" 7 | 8 | repositories { 9 | mavenCentral() 10 | } 11 | 12 | @OptIn(org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi::class) 13 | kotlin { 14 | defaultConfig() 15 | allTargets() 16 | sourceSets { 17 | val commonMain by getting { 18 | dependencies { 19 | addModules(SupabaseModule.AUTH) 20 | api(project(":postgrest-kt")) 21 | api(libs.ktor.client.websockets) 22 | } 23 | } 24 | val commonTest by getting { 25 | dependencies { 26 | implementation(project(":test-common")) 27 | implementation(libs.bundles.testing) 28 | implementation(libs.turbine) 29 | } 30 | } 31 | } 32 | } 33 | 34 | configureLibraryAndroidTarget() -------------------------------------------------------------------------------- /Realtime/src/androidMain/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/RealtimeCallback.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.realtime 2 | 3 | import io.github.jan.supabase.annotations.SupabaseInternal 4 | import kotlinx.serialization.json.JsonObject 5 | 6 | @SupabaseInternal 7 | sealed interface RealtimeCallback { 8 | 9 | val callback: (T) -> Unit 10 | val id: Long 11 | 12 | class PostgresCallback( 13 | override val callback: (PostgresAction) -> Unit, 14 | val filter: PostgresJoinConfig, 15 | override val id: Long 16 | ): RealtimeCallback 17 | 18 | class BroadcastCallback( 19 | override val callback: (JsonObject) -> Unit, 20 | val event: String, 21 | override val id: Long 22 | ): RealtimeCallback 23 | 24 | class PresenceCallback( 25 | override val callback: (PresenceAction) -> Unit, 26 | override val id: Long 27 | ): RealtimeCallback 28 | 29 | } -------------------------------------------------------------------------------- /Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/RealtimeMessage.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.realtime 2 | 3 | import io.github.jan.supabase.annotations.SupabaseInternal 4 | import kotlinx.serialization.Serializable 5 | import kotlinx.serialization.json.JsonObject 6 | 7 | /** 8 | * Represents a message retrieved by the [RealtimeChannel] 9 | */ 10 | @Serializable 11 | @SupabaseInternal 12 | data class RealtimeMessage(val topic: String, val event: String, val payload: JsonObject, val ref: String?) -------------------------------------------------------------------------------- /Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/RealtimeTopic.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.realtime 2 | 3 | @PublishedApi 4 | internal object RealtimeTopic { 5 | 6 | const val PREFIX = "realtime" 7 | 8 | fun withChannelId(channelId: String): String { 9 | return "$PREFIX:$channelId" 10 | } 11 | 12 | } -------------------------------------------------------------------------------- /Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/annotations/ChannelDsl.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.realtime.annotations 2 | 3 | /** 4 | * Used to mark Channel DSL functions 5 | */ 6 | @Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.TYPE) 7 | @DslMarker 8 | annotation class ChannelDsl 9 | -------------------------------------------------------------------------------- /Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/data/BroadcastApiBody.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.realtime.data 2 | 3 | import kotlinx.serialization.SerialName 4 | import kotlinx.serialization.Serializable 5 | import kotlinx.serialization.json.JsonObject 6 | 7 | @Serializable 8 | internal data class BroadcastApiBody( 9 | val messages: List 10 | ) 11 | 12 | @Serializable 13 | internal data class BroadcastApiMessage( 14 | val topic: String, 15 | val event: String, 16 | val payload: JsonObject, 17 | @SerialName("private") 18 | val isPrivate: Boolean 19 | ) 20 | -------------------------------------------------------------------------------- /Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/data/PostgresActionData.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.realtime.data 2 | 3 | import io.github.jan.supabase.realtime.Column 4 | import kotlinx.datetime.Instant 5 | import kotlinx.serialization.SerialName 6 | import kotlinx.serialization.Serializable 7 | import kotlinx.serialization.json.JsonObject 8 | 9 | @Serializable 10 | internal data class PostgresActionData( 11 | val record: JsonObject? = null, 12 | @SerialName("old_record") 13 | val oldRecord: JsonObject? = null, 14 | val columns: List, 15 | @SerialName("commit_timestamp") 16 | val commitTimestamp: Instant 17 | ) -------------------------------------------------------------------------------- /Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/event/RBroadcastEvent.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.realtime.event 2 | 3 | import io.github.jan.supabase.realtime.RealtimeChannel 4 | import io.github.jan.supabase.realtime.RealtimeMessage 5 | import kotlinx.serialization.json.JsonObject 6 | import kotlinx.serialization.json.jsonObject 7 | import kotlinx.serialization.json.jsonPrimitive 8 | 9 | /** 10 | * Handles broadcast events 11 | */ 12 | data object RBroadcastEvent : RealtimeEvent { 13 | 14 | override suspend fun handle(channel: RealtimeChannel, message: RealtimeMessage) { 15 | val event = message.payload["event"]?.jsonPrimitive?.content ?: "" 16 | channel.callbackManager.triggerBroadcast(event, message.payload["payload"]?.jsonObject ?: JsonObject(mutableMapOf())) 17 | } 18 | 19 | override fun appliesTo(message: RealtimeMessage): Boolean { 20 | return message.event == RealtimeChannel.CHANNEL_EVENT_BROADCAST 21 | } 22 | 23 | } -------------------------------------------------------------------------------- /Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/event/RCloseEvent.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.realtime.event 2 | 3 | import io.github.jan.supabase.logging.d 4 | import io.github.jan.supabase.realtime.Realtime 5 | import io.github.jan.supabase.realtime.RealtimeChannel 6 | import io.github.jan.supabase.realtime.RealtimeMessage 7 | 8 | /** 9 | * Event that handles the closing of a channel 10 | */ 11 | data object RCloseEvent : RealtimeEvent { 12 | 13 | override suspend fun handle(channel: RealtimeChannel, message: RealtimeMessage) { 14 | channel.realtime.removeChannel(channel) 15 | Realtime.logger.d { "Unsubscribed from channel ${message.topic}" } 16 | } 17 | 18 | override fun appliesTo(message: RealtimeMessage): Boolean { 19 | return message.event == RealtimeChannel.CHANNEL_EVENT_CLOSE 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/event/RErrorEvent.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.realtime.event 2 | 3 | import io.github.jan.supabase.logging.e 4 | import io.github.jan.supabase.realtime.Realtime 5 | import io.github.jan.supabase.realtime.RealtimeChannel 6 | import io.github.jan.supabase.realtime.RealtimeMessage 7 | 8 | /** 9 | * Event that handles an error event 10 | */ 11 | data object RErrorEvent : RealtimeEvent { 12 | 13 | override suspend fun handle(channel: RealtimeChannel, message: RealtimeMessage) { 14 | Realtime.logger.e { "Received an error in channel ${message.topic}. That could be as a result of an invalid access token" } 15 | } 16 | 17 | override fun appliesTo(message: RealtimeMessage): Boolean { 18 | return message.event == RealtimeChannel.CHANNEL_EVENT_ERROR 19 | } 20 | 21 | } -------------------------------------------------------------------------------- /Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/event/RPresenceStateEvent.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.realtime.event 2 | 3 | import io.github.jan.supabase.decodeIfNotEmptyOrDefault 4 | import io.github.jan.supabase.realtime.Presence 5 | import io.github.jan.supabase.realtime.RealtimeChannel 6 | import io.github.jan.supabase.realtime.RealtimeMessage 7 | 8 | /** 9 | * Event that handles the presence state event 10 | */ 11 | data object RPresenceStateEvent : RealtimeEvent { 12 | 13 | override suspend fun handle(channel: RealtimeChannel, message: RealtimeMessage) { 14 | val joins = message.payload.decodeIfNotEmptyOrDefault(mapOf()) 15 | channel.callbackManager.triggerPresenceDiff(joins, mapOf()) 16 | } 17 | 18 | override fun appliesTo(message: RealtimeMessage): Boolean { 19 | return message.event == RealtimeChannel.CHANNEL_EVENT_PRESENCE_STATE 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/event/RSystemEvent.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.realtime.event 2 | 3 | import io.github.jan.supabase.logging.d 4 | import io.github.jan.supabase.realtime.Realtime 5 | import io.github.jan.supabase.realtime.RealtimeChannel 6 | import io.github.jan.supabase.realtime.RealtimeMessage 7 | import kotlinx.serialization.json.jsonPrimitive 8 | 9 | /** 10 | * Event that handles the system event 11 | */ 12 | data object RSystemEvent : RealtimeEvent { 13 | 14 | override suspend fun handle(channel: RealtimeChannel, message: RealtimeMessage) { 15 | Realtime.logger.d { "Subscribed to channel ${message.topic}" } 16 | channel.updateStatus(RealtimeChannel.Status.SUBSCRIBED) 17 | } 18 | 19 | override fun appliesTo(message: RealtimeMessage): Boolean { 20 | return message.event == RealtimeChannel.CHANNEL_EVENT_SYSTEM && message.payload["status"]?.jsonPrimitive?.content == "ok" 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/event/RTokenExpiredEvent.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.realtime.event 2 | 3 | import io.github.jan.supabase.logging.w 4 | import io.github.jan.supabase.realtime.Realtime 5 | import io.github.jan.supabase.realtime.RealtimeChannel 6 | import io.github.jan.supabase.realtime.RealtimeMessage 7 | import kotlinx.serialization.json.jsonPrimitive 8 | 9 | /** 10 | * Event that handles the token expired event 11 | */ 12 | data object RTokenExpiredEvent : RealtimeEvent { 13 | 14 | override suspend fun handle(channel: RealtimeChannel, message: RealtimeMessage) { 15 | Realtime.logger.w { "Received token expired event. This should not happen, please report this warning." } 16 | } 17 | 18 | override fun appliesTo(message: RealtimeMessage): Boolean { 19 | return message.event == RealtimeChannel.CHANNEL_EVENT_SYSTEM && message.payload["message"]?.jsonPrimitive?.content?.contains("access token has expired") ?: false 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/websocket/KtorRealtimeWebsocketFactory.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.realtime.websocket 2 | 3 | import io.ktor.client.HttpClient 4 | import io.ktor.client.plugins.websocket.webSocketSession 5 | 6 | /** 7 | * Implementation of [RealtimeWebsocketFactory] using Ktor's [HttpClient]. 8 | */ 9 | class KtorRealtimeWebsocketFactory( 10 | private val httpClient: HttpClient, 11 | ): RealtimeWebsocketFactory { 12 | 13 | override suspend fun create(url: String): RealtimeWebsocket { 14 | val ws = httpClient.webSocketSession(url) 15 | return KtorRealtimeWebsocket(ws) 16 | } 17 | 18 | } -------------------------------------------------------------------------------- /Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/websocket/RealtimeWebsocketFactory.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.realtime.websocket 2 | 3 | import io.github.jan.supabase.annotations.SupabaseInternal 4 | 5 | /** 6 | * Interface for creating a websocket connection to the Supabase Realtime service. 7 | */ 8 | @OptIn(ExperimentalSubclassOptIn::class) 9 | @SubclassOptInRequired(SupabaseInternal::class) 10 | interface RealtimeWebsocketFactory { 11 | 12 | /** 13 | * Create a new websocket connection to the given URL. 14 | * @param url The URL to connect to. 15 | */ 16 | suspend fun create(url: String): RealtimeWebsocket 17 | 18 | } -------------------------------------------------------------------------------- /Realtime/src/commonTest/kotlin/RealtimeTopicTest.kt: -------------------------------------------------------------------------------- 1 | import io.github.jan.supabase.realtime.RealtimeTopic 2 | import kotlin.test.Test 3 | import kotlin.test.assertEquals 4 | 5 | class RealtimeTopicTest { 6 | 7 | @Test 8 | fun testRealtimeTopic() { 9 | val channelId = "channelId" 10 | val topic = RealtimeTopic.withChannelId(channelId) 11 | assertEquals("realtime:channelId", topic) 12 | } 13 | 14 | @Test 15 | fun testRealtimePrefix() { 16 | assertEquals("realtime", RealtimeTopic.PREFIX) 17 | } 18 | 19 | } -------------------------------------------------------------------------------- /Storage/src/androidMain/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 11 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Storage/src/androidMain/kotlin/io/github/jan/supabase/storage/Context.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.storage 2 | 3 | import android.content.Context 4 | import androidx.startup.Initializer 5 | 6 | private var appContext: Context? = null 7 | 8 | internal class SupabaseInitializer : Initializer { 9 | override fun create(context: Context): Context = context.applicationContext.also { appContext = it } 10 | 11 | override fun dependencies(): List>> = emptyList() 12 | 13 | } 14 | 15 | internal fun applicationContext(): Context = appContext ?: error("Application context not initialized") -------------------------------------------------------------------------------- /Storage/src/commonMain/kotlin/io/github/jan/supabase/storage/DownloadOptionBuilder.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.storage 2 | 3 | import io.github.jan.supabase.network.HttpRequestOverride 4 | 5 | /** 6 | * Builder for downloading files with additional options 7 | */ 8 | class DownloadOptionBuilder( 9 | internal var transform: ImageTransformation.() -> Unit = {}, 10 | internal val httpRequestOverrides: MutableList = mutableListOf() 11 | ) { 12 | 13 | /** 14 | * Transforms the image before downloading 15 | * @param transform The transformation to apply 16 | */ 17 | fun transform(transform: ImageTransformation.() -> Unit) { 18 | this.transform = transform 19 | } 20 | 21 | /** 22 | * Overrides the HTTP request 23 | * @param override The override to apply 24 | */ 25 | fun httpOverride(override: HttpRequestOverride) { 26 | httpRequestOverrides.add(override) 27 | } 28 | 29 | } -------------------------------------------------------------------------------- /Storage/src/commonMain/kotlin/io/github/jan/supabase/storage/FileSizeLimit.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.storage 2 | 3 | import kotlin.jvm.JvmInline 4 | 5 | /** 6 | * A file size limit for buckets 7 | * @param value The value of the limit (e.g. 10mb) 8 | */ 9 | @JvmInline 10 | value class FileSizeLimit internal constructor(val value: String) -------------------------------------------------------------------------------- /Storage/src/commonMain/kotlin/io/github/jan/supabase/storage/FileUploadResponse.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.storage 2 | 3 | /** 4 | * The response of a file upload 5 | * @param id The id of the file 6 | * @param path The path to the file. Can be used as is in [BucketApi] uploading methods 7 | * @param key The key of the file 8 | */ 9 | data class FileUploadResponse( 10 | val id: String? = null, 11 | val path: String, 12 | val key: String? = null 13 | ) 14 | -------------------------------------------------------------------------------- /Storage/src/commonMain/kotlin/io/github/jan/supabase/storage/SignedUrl.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.storage 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | /** 6 | * Represents a signed url 7 | * @param error An optional error message 8 | * @param signedURL The signed url 9 | * @param path The path of the file 10 | */ 11 | @Serializable 12 | data class SignedUrl(val error: String? = null, val signedURL: String, val path: String) 13 | -------------------------------------------------------------------------------- /Storage/src/commonMain/kotlin/io/github/jan/supabase/storage/StorageErrorResponse.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.storage 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | @Serializable 6 | internal data class StorageErrorResponse( 7 | val statusCode: Int, 8 | val error: String, 9 | val message: String 10 | ) 11 | -------------------------------------------------------------------------------- /Storage/src/commonMain/kotlin/io/github/jan/supabase/storage/StorageItem.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.storage 2 | 3 | /** 4 | * Represents a file in the storage bucket. 5 | * @param path The path of the file. 6 | * @param bucketId The id of the bucket. 7 | * @param authenticated Whether the file has to be accessed authenticated or not. 8 | */ 9 | data class StorageItem( 10 | val path: String, 11 | val bucketId: String, 12 | val authenticated: Boolean 13 | ) 14 | 15 | /** 16 | * Creates a [StorageItem] for an authenticated file. 17 | */ 18 | fun authenticatedStorageItem(bucketId: String, path: String) = StorageItem(path, bucketId, true) 19 | 20 | /** 21 | * Creates a [StorageItem] for a public file. 22 | */ 23 | fun publicStorageItem(bucketId: String, path: String) = StorageItem(path, bucketId, false) -------------------------------------------------------------------------------- /Storage/src/commonMain/kotlin/io/github/jan/supabase/storage/UploadData.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.storage 2 | 3 | import io.ktor.utils.io.ByteReadChannel 4 | 5 | /** 6 | * Represents the data to upload 7 | * @param stream The [ByteReadChannel] for streaming the data 8 | * @param size The size of the data 9 | */ 10 | data class UploadData(val stream: ByteReadChannel, val size: Long) -------------------------------------------------------------------------------- /Storage/src/commonMain/kotlin/io/github/jan/supabase/storage/UploadSignedUrl.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.storage 2 | 3 | /** 4 | * A signed url to upload a file 5 | * @param url The signed url 6 | * @param path The path of the file 7 | * @param token The token to use for the upload 8 | */ 9 | data class UploadSignedUrl( 10 | val url: String, 11 | val path: String, 12 | val token: String 13 | ) 14 | -------------------------------------------------------------------------------- /Storage/src/commonMain/kotlin/io/github/jan/supabase/storage/Utils.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.storage 2 | 3 | import kotlinx.serialization.json.JsonObjectBuilder 4 | import kotlinx.serialization.json.put 5 | 6 | internal fun JsonObjectBuilder.putImageTransformation(transformation: ImageTransformation) { 7 | transformation.width?.let { put("width", it) } 8 | transformation.height?.let { put("height", it) } 9 | transformation.resize?.let { put("resize", it.name.lowercase()) } 10 | transformation.quality?.let { put("quality", it) } 11 | transformation.format?.let { put("format", it) } 12 | } -------------------------------------------------------------------------------- /Storage/src/commonMain/kotlin/io/github/jan/supabase/storage/resumable/StreamContent.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.storage.resumable 2 | 3 | import io.github.jan.supabase.annotations.SupabaseInternal 4 | import io.ktor.http.ContentType 5 | import io.ktor.http.content.OutgoingContent 6 | import io.ktor.utils.io.ByteWriteChannel 7 | 8 | @SupabaseInternal 9 | internal class StreamContent( 10 | size: Long, 11 | private val copyTo: suspend ByteWriteChannel.() -> Unit 12 | ) : OutgoingContent.WriteChannelContent() { 13 | 14 | override val contentLength: Long = size 15 | override val contentType: ContentType = ContentType.parse("application/offset+octet-stream") 16 | 17 | override suspend fun writeTo(channel: ByteWriteChannel) { 18 | copyTo(channel) 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /Storage/src/linuxMain/kotlin/io/github/jan/supabase/storage/resumable/ResumableCacheUtil.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.storage.resumable 2 | 3 | import io.github.jan.supabase.annotations.SupabaseInternal 4 | 5 | @SupabaseInternal 6 | actual fun createDefaultResumableCache(): ResumableCache = MemoryResumableCache() -------------------------------------------------------------------------------- /Supabase/src/androidMain/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Supabase/src/androidMain/kotlin/io/github/jan/supabase/DefaultDispatcher.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase 2 | 3 | import kotlinx.coroutines.Dispatchers 4 | 5 | actual val defaultDispatcher = Dispatchers.IO -------------------------------------------------------------------------------- /Supabase/src/androidMain/kotlin/io/github/jan/supabase/PlatformTarget.android.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase 2 | 3 | internal actual fun getOSInformation(): OSInformation? = OSInformation( 4 | name = "Android", 5 | version = android.os.Build.VERSION.RELEASE 6 | ) -------------------------------------------------------------------------------- /Supabase/src/androidMain/kotlin/io/github/jan/supabase/PlatformTarget.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase 2 | 3 | /** 4 | * The current target platform 5 | */ 6 | actual val CurrentPlatformTarget = PlatformTarget.ANDROID -------------------------------------------------------------------------------- /Supabase/src/androidUnitTest/kotlin/PlatformTargetTest.kt: -------------------------------------------------------------------------------- 1 | import io.github.jan.supabase.CurrentPlatformTarget 2 | import io.github.jan.supabase.PlatformTarget 3 | import org.junit.Test 4 | import kotlin.test.assertEquals 5 | 6 | actual class PlatformTargetTest { 7 | 8 | @Test 9 | actual fun testPlatformTarget() { 10 | assertEquals(PlatformTarget.ANDROID, CurrentPlatformTarget, "The current platform target should be ANDROID") 11 | } 12 | 13 | } -------------------------------------------------------------------------------- /Supabase/src/appleMain/kotlin/io/supabase/supabase/Utils.kt: -------------------------------------------------------------------------------- 1 | package io.supabase.supabase 2 | 3 | import kotlinx.cinterop.ExperimentalForeignApi 4 | import kotlinx.cinterop.UnsafeNumber 5 | import kotlinx.cinterop.useContents 6 | 7 | @OptIn(ExperimentalForeignApi::class, UnsafeNumber::class) 8 | internal fun getOSVersion(): String { 9 | val processInfo = platform.Foundation.NSProcessInfo.processInfo 10 | return processInfo.operatingSystemVersion.useContents { 11 | val majorVersion = this.majorVersion 12 | val minorVersion = this.minorVersion 13 | val patchVersion = this.patchVersion 14 | buildString { 15 | append(majorVersion) 16 | append(".") 17 | append(minorVersion) 18 | append(".") 19 | append(patchVersion) 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /Supabase/src/commonMain/kotlin/io/github/jan/supabase/AccessTokenProvider.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase 2 | 3 | /** 4 | * Optional function for using a third-party authentication system with 5 | * Supabase. The function should return an access token or ID token (JWT) by 6 | * obtaining it from the third-party auth client library. Note that this 7 | * function may be called concurrently and many times. Use memoization and 8 | * locking techniques if this is not supported by the client libraries. 9 | */ 10 | typealias AccessTokenProvider = suspend () -> String? -------------------------------------------------------------------------------- /Supabase/src/commonMain/kotlin/io/github/jan/supabase/DefaultDispatcher.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase 2 | 3 | import kotlinx.coroutines.CoroutineDispatcher 4 | 5 | internal expect val defaultDispatcher: CoroutineDispatcher -------------------------------------------------------------------------------- /Supabase/src/commonMain/kotlin/io/github/jan/supabase/SupabaseClientConfig.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase 2 | 3 | import io.github.jan.supabase.logging.LogLevel 4 | import io.ktor.client.engine.HttpClientEngine 5 | import kotlinx.coroutines.CoroutineDispatcher 6 | import kotlin.time.Duration 7 | 8 | internal data class SupabaseClientConfig( 9 | val supabaseUrl: String, 10 | val supabaseKey: String, 11 | val defaultLogLevel: LogLevel, 12 | val networkConfig: SupabaseNetworkConfig, 13 | val defaultSerializer: SupabaseSerializer, 14 | val coroutineDispatcher: CoroutineDispatcher, 15 | val accessToken: AccessTokenProvider?, 16 | val plugins: Map, 17 | val osInformation: OSInformation? 18 | ) 19 | 20 | internal data class SupabaseNetworkConfig( 21 | val useHTTPS: Boolean, 22 | val httpEngine: HttpClientEngine?, 23 | val httpConfigOverrides: List, 24 | val requestTimeout: Duration 25 | ) 26 | -------------------------------------------------------------------------------- /Supabase/src/commonMain/kotlin/io/github/jan/supabase/annotations/SupabaseDsl.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.annotations 2 | 3 | /** 4 | * Used to mark DSL functions 5 | */ 6 | @Target(AnnotationTarget.CLASS, AnnotationTarget.TYPE, AnnotationTarget.FUNCTION) 7 | @DslMarker 8 | annotation class SupabaseDsl 9 | -------------------------------------------------------------------------------- /Supabase/src/commonMain/kotlin/io/github/jan/supabase/annotations/SupabaseExperimental.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.annotations 2 | 3 | /** 4 | * Used to mark experimental APIs 5 | */ 6 | @RequiresOptIn(level = RequiresOptIn.Level.ERROR, message = "This API is experimental and may not be stable yet") 7 | annotation class SupabaseExperimental 8 | -------------------------------------------------------------------------------- /Supabase/src/commonMain/kotlin/io/github/jan/supabase/annotations/SupabaseInternal.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.annotations 2 | 3 | /** 4 | * Used to mark internal APIs 5 | */ 6 | @RequiresOptIn(level = RequiresOptIn.Level.ERROR, message = "This API is internal and can change at any time") 7 | annotation class SupabaseInternal 8 | -------------------------------------------------------------------------------- /Supabase/src/commonMain/kotlin/io/github/jan/supabase/exceptions/HttpRequestException.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.exceptions 2 | 3 | import io.ktor.client.request.HttpRequestBuilder 4 | import kotlinx.io.IOException 5 | 6 | /** 7 | * An exception that is thrown when a request fails due to network issues 8 | */ 9 | class HttpRequestException(message: String, request: HttpRequestBuilder): IOException("HTTP request to ${request.url.buildString()} (${request.method.value}) failed with message: $message") -------------------------------------------------------------------------------- /Supabase/src/commonMain/kotlin/io/github/jan/supabase/exceptions/SupabaseEncodingException.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.exceptions 2 | 3 | /** 4 | * Thrown when an encoding error occurs 5 | */ 6 | class SupabaseEncodingException(message: String): Exception(message) -------------------------------------------------------------------------------- /Supabase/src/commonMain/kotlin/io/github/jan/supabase/logging/LogLevel.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.logging 2 | 3 | /** 4 | * Represents a log level 5 | * 6 | */ 7 | enum class LogLevel { 8 | /** 9 | * Debug log level 10 | */ 11 | DEBUG, 12 | 13 | /** 14 | * Info log level 15 | */ 16 | INFO, 17 | 18 | /** 19 | * Warning log level 20 | */ 21 | WARNING, 22 | /** 23 | * Error log level 24 | */ 25 | ERROR, 26 | 27 | /** 28 | * No log level 29 | */ 30 | NONE, 31 | } -------------------------------------------------------------------------------- /Supabase/src/commonMain/kotlin/io/github/jan/supabase/plugins/CustomSerializationConfig.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.plugins 2 | 3 | import io.github.jan.supabase.SupabaseClientBuilder 4 | import io.github.jan.supabase.SupabaseSerializer 5 | 6 | /** 7 | * A configuration for a plugin, which allows to customize the serialization 8 | */ 9 | interface CustomSerializationConfig { 10 | 11 | /** 12 | * The serializer used for this module. Defaults to [SupabaseClientBuilder.defaultSerializer], when null. 13 | */ 14 | var serializer: SupabaseSerializer? 15 | 16 | } -------------------------------------------------------------------------------- /Supabase/src/commonMain/kotlin/io/github/jan/supabase/plugins/CustomSerializationPlugin.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.plugins 2 | 3 | import io.github.jan.supabase.SupabaseClientBuilder 4 | import io.github.jan.supabase.SupabaseSerializer 5 | 6 | /** 7 | * A plugin, which allows to customize the serialization 8 | */ 9 | interface CustomSerializationPlugin { 10 | 11 | /** 12 | * The serializer used for this module. Defaults to [SupabaseClientBuilder.defaultSerializer], when null. 13 | */ 14 | val serializer: SupabaseSerializer 15 | 16 | } -------------------------------------------------------------------------------- /Supabase/src/commonMain/kotlin/io/github/jan/supabase/plugins/SerializableData.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.plugins 2 | 3 | import io.github.jan.supabase.SupabaseSerializer 4 | import io.github.jan.supabase.annotations.SupabaseInternal 5 | 6 | /** 7 | * An interface for data that can be serialized with [SupabaseSerializer] 8 | */ 9 | interface SerializableData { 10 | 11 | /** 12 | * The serializer used to serialize this data 13 | */ 14 | @SupabaseInternal 15 | val serializer: SupabaseSerializer 16 | 17 | } -------------------------------------------------------------------------------- /Supabase/src/commonMain/kotlin/io/github/jan/supabase/plugins/SupabasePlugin.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.plugins 2 | 3 | import io.github.jan.supabase.SupabaseClient 4 | 5 | /** 6 | * A plugin is a feature that can be installed into the supabase client 7 | */ 8 | interface SupabasePlugin { 9 | 10 | /** 11 | * The config for this plugin 12 | */ 13 | val config: Config 14 | 15 | /** 16 | * The corresponding [SupabaseClient] 17 | */ 18 | val supabaseClient: SupabaseClient 19 | 20 | /** 21 | * Free all resources used by this plugin 22 | */ 23 | suspend fun close() {} 24 | 25 | /** 26 | * Initialize the plugin. Use this function, if you want to execute code after the plugin has been created but also have to access other plugins. 27 | * 28 | * **Note:** This function is called by the [SupabaseClient] after all plugins have been created. Do not call this function manually. 29 | */ 30 | fun init() {} 31 | 32 | } -------------------------------------------------------------------------------- /Supabase/src/commonMain/kotlin/io/github/jan/supabase/serializer/KotlinXSerializer.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.serializer 2 | 3 | import io.github.jan.supabase.SupabaseSerializer 4 | import kotlinx.serialization.json.Json 5 | import kotlinx.serialization.serializer 6 | import kotlin.reflect.KType 7 | 8 | /** 9 | * A [SupabaseSerializer] that uses kotlinx.serialization 10 | */ 11 | class KotlinXSerializer(private val json: Json = Json) : SupabaseSerializer { 12 | 13 | override fun encode(type: KType, value: T): String = json.encodeToString(json.serializersModule.serializer(type), value) 14 | 15 | @Suppress("UNCHECKED_CAST") 16 | override fun decode(type: KType, value: String): T = 17 | json.decodeFromString(json.serializersModule.serializer(type), value) as T 18 | 19 | } -------------------------------------------------------------------------------- /Supabase/src/commonTest/kotlin/PlatformTargetTest.kt: -------------------------------------------------------------------------------- 1 | import kotlin.test.Test 2 | 3 | expect class PlatformTargetTest { 4 | 5 | @Test 6 | fun testPlatformTarget() 7 | 8 | } -------------------------------------------------------------------------------- /Supabase/src/iosMain/kotlin/io/github/jan/supabase/DefaultDispatcher.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase 2 | 3 | import kotlinx.coroutines.Dispatchers 4 | import kotlinx.coroutines.IO 5 | 6 | actual val defaultDispatcher = Dispatchers.IO -------------------------------------------------------------------------------- /Supabase/src/iosMain/kotlin/io/github/jan/supabase/PlatformTarget.ios.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase 2 | 3 | import io.supabase.supabase.getOSVersion 4 | 5 | internal actual fun getOSInformation(): OSInformation? = OSInformation( 6 | name = "iOS", 7 | version = getOSVersion() 8 | ) -------------------------------------------------------------------------------- /Supabase/src/iosMain/kotlin/io/github/jan/supabase/PlatformTarget.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase 2 | 3 | /** 4 | * The current target platform 5 | */ 6 | actual val CurrentPlatformTarget = PlatformTarget.IOS -------------------------------------------------------------------------------- /Supabase/src/iosTest/kotlin/PlatformTargetTest.kt: -------------------------------------------------------------------------------- 1 | import io.github.jan.supabase.CurrentPlatformTarget 2 | import io.github.jan.supabase.PlatformTarget 3 | import kotlin.test.Test 4 | import kotlin.test.assertEquals 5 | 6 | actual class PlatformTargetTest { 7 | 8 | @Test 9 | actual fun testPlatformTarget() { 10 | assertEquals(PlatformTarget.IOS, CurrentPlatformTarget, "The current platform target should be IOS") 11 | } 12 | 13 | } -------------------------------------------------------------------------------- /Supabase/src/jsMain/kotlin/io/github/jan/supabase/DefaultDispatcher.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase 2 | 3 | import kotlinx.coroutines.Dispatchers 4 | 5 | actual val defaultDispatcher = Dispatchers.Default -------------------------------------------------------------------------------- /Supabase/src/jsMain/kotlin/io/github/jan/supabase/PlatformTarget.js.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase 2 | 3 | internal actual fun getOSInformation(): OSInformation? = null -------------------------------------------------------------------------------- /Supabase/src/jsMain/kotlin/io/github/jan/supabase/PlatformTarget.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase 2 | 3 | /** 4 | * The current target platform 5 | */ 6 | actual val CurrentPlatformTarget: PlatformTarget = PlatformTarget.JS -------------------------------------------------------------------------------- /Supabase/src/jsTest/kotlin/PlatformTargetTest.kt: -------------------------------------------------------------------------------- 1 | import io.github.jan.supabase.CurrentPlatformTarget 2 | import io.github.jan.supabase.PlatformTarget 3 | import kotlin.test.Test 4 | import kotlin.test.assertEquals 5 | 6 | actual class PlatformTargetTest { 7 | 8 | @Test 9 | actual fun testPlatformTarget() { 10 | assertEquals(PlatformTarget.JS, CurrentPlatformTarget, "The current platform target should be WEB") 11 | } 12 | 13 | } -------------------------------------------------------------------------------- /Supabase/src/jvmMain/kotlin/io/github/jan/supabase/DefaultDispatcher.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase 2 | 3 | import kotlinx.coroutines.Dispatchers 4 | 5 | actual val defaultDispatcher = Dispatchers.IO -------------------------------------------------------------------------------- /Supabase/src/jvmMain/kotlin/io/github/jan/supabase/PlatformTarget.jvm.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase 2 | 3 | internal actual fun getOSInformation(): OSInformation? = OSInformation( 4 | name = System.getProperty("os.name"), 5 | version = System.getProperty("os.version") 6 | ) -------------------------------------------------------------------------------- /Supabase/src/jvmMain/kotlin/io/github/jan/supabase/PlatformTarget.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase 2 | 3 | /** 4 | * The current target platform 5 | */ 6 | actual val CurrentPlatformTarget = PlatformTarget.JVM -------------------------------------------------------------------------------- /Supabase/src/jvmTest/kotlin/PlatformTargetTest.kt: -------------------------------------------------------------------------------- 1 | import io.github.jan.supabase.CurrentPlatformTarget 2 | import io.github.jan.supabase.PlatformTarget 3 | import io.github.jan.supabase.getOSInformation 4 | import kotlin.test.Test 5 | import kotlin.test.assertEquals 6 | 7 | actual class PlatformTargetTest { 8 | 9 | @Test 10 | actual fun testPlatformTarget() { 11 | assertEquals(PlatformTarget.JVM, CurrentPlatformTarget, "The current platform target should be DESKTOP") 12 | } 13 | 14 | @Test 15 | fun testGetOSInformation() { 16 | System.setProperty("os.name", "Linux") 17 | System.setProperty("os.version", "5.4.0") 18 | val osInfo = getOSInformation() 19 | assertEquals("Linux", osInfo?.name, "OS name should be Linux") 20 | assertEquals("5.4.0", osInfo?.version, "OS version should be 5.4.0") 21 | } 22 | 23 | } -------------------------------------------------------------------------------- /Supabase/src/linuxMain/kotlin/io/github/jan/supabase/DefaultDispatcher.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase 2 | 3 | import kotlinx.coroutines.Dispatchers 4 | import kotlinx.coroutines.IO 5 | 6 | actual val defaultDispatcher = Dispatchers.IO -------------------------------------------------------------------------------- /Supabase/src/linuxMain/kotlin/io/github/jan/supabase/PlatformTarget.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase 2 | 3 | /** 4 | * The current target platform 5 | */ 6 | actual val CurrentPlatformTarget = PlatformTarget.LINUX -------------------------------------------------------------------------------- /Supabase/src/linuxMain/kotlin/io/github/jan/supabase/PlatformTarget.linux.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase 2 | 3 | import kotlinx.cinterop.ExperimentalForeignApi 4 | import kotlinx.cinterop.convert 5 | import kotlinx.cinterop.refTo 6 | import platform.posix.fclose 7 | import platform.posix.fopen 8 | import platform.posix.fread 9 | 10 | internal actual fun getOSInformation(): OSInformation? = OSInformation( 11 | name = "Linux", 12 | version = getOSVersion() 13 | ) 14 | 15 | private fun getOSVersion(): String = readProcVersion() ?: "Unknown" 16 | 17 | @OptIn(ExperimentalForeignApi::class) 18 | fun readProcVersion(): String? { 19 | val path = "/proc/version" 20 | val file = fopen(path, "r") ?: return null 21 | try { 22 | val buffer = ByteArray(1024) 23 | val bytesRead = fread(buffer.refTo(0), 1.convert(), buffer.size.convert(), file).toInt() 24 | return buffer.decodeToString(endIndex = bytesRead).trim() 25 | } finally { 26 | fclose(file) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Supabase/src/linuxTest/kotlin/PlatformTargetTest.kt: -------------------------------------------------------------------------------- 1 | import io.github.jan.supabase.CurrentPlatformTarget 2 | import io.github.jan.supabase.PlatformTarget 3 | import kotlin.test.Test 4 | import kotlin.test.assertEquals 5 | 6 | actual class PlatformTargetTest { 7 | 8 | @Test 9 | actual fun testPlatformTarget() { 10 | assertEquals(PlatformTarget.LINUX, CurrentPlatformTarget, "The current platform target should be LINUX") 11 | } 12 | 13 | } -------------------------------------------------------------------------------- /Supabase/src/macosMain/kotlin/io/github/jan/supabase/DefaultDispatcher.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase 2 | 3 | import kotlinx.coroutines.Dispatchers 4 | import kotlinx.coroutines.IO 5 | 6 | actual val defaultDispatcher = Dispatchers.IO -------------------------------------------------------------------------------- /Supabase/src/macosMain/kotlin/io/github/jan/supabase/PlatformTarget.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase 2 | 3 | /** 4 | * The current target platform 5 | */ 6 | actual val CurrentPlatformTarget = PlatformTarget.MACOS -------------------------------------------------------------------------------- /Supabase/src/macosMain/kotlin/io/github/jan/supabase/PlatformTarget.macos.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase 2 | 3 | import io.supabase.supabase.getOSVersion 4 | 5 | internal actual fun getOSInformation(): OSInformation? = OSInformation( 6 | name = "macOS", 7 | version = getOSVersion() 8 | ) -------------------------------------------------------------------------------- /Supabase/src/macosTest/kotlin/PlatformTargetTest.kt: -------------------------------------------------------------------------------- 1 | import io.github.jan.supabase.CurrentPlatformTarget 2 | import io.github.jan.supabase.PlatformTarget 3 | import kotlin.test.Test 4 | import kotlin.test.assertEquals 5 | 6 | actual class PlatformTargetTest { 7 | 8 | @Test 9 | actual fun testPlatformTarget() { 10 | assertEquals(PlatformTarget.MACOS, CurrentPlatformTarget, "The current platform target should be MACOS") 11 | } 12 | 13 | } -------------------------------------------------------------------------------- /Supabase/src/mingwMain/kotlin/io/github/jan/supabase/PlatformTarget.mingw.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase 2 | 3 | import platform.windows.DWORD 4 | import platform.windows.GetVersion 5 | 6 | internal actual fun getOSInformation(): OSInformation? = OSInformation( 7 | name = "Windows", 8 | version = getOSVersion() 9 | ) 10 | 11 | private fun getOSVersion(): String { 12 | val dwVersion: DWORD = GetVersion() 13 | 14 | val dwMajorVersion = dwVersion and 255u 15 | val dwMinorVersion = (dwVersion shr 8) and 255u 16 | return "$dwMajorVersion.$dwMinorVersion" 17 | } -------------------------------------------------------------------------------- /Supabase/src/mingwX64Main/kotlin/io/github/jan/supabase/CurrentPlatformTarget.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase 2 | 3 | /** 4 | * The current target platform 5 | */ 6 | actual val CurrentPlatformTarget: PlatformTarget = PlatformTarget.WINDOWS -------------------------------------------------------------------------------- /Supabase/src/mingwX64Main/kotlin/io/github/jan/supabase/DefaultDispatcher.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase 2 | 3 | import kotlinx.coroutines.Dispatchers 4 | import kotlinx.coroutines.IO 5 | 6 | actual val defaultDispatcher = Dispatchers.IO -------------------------------------------------------------------------------- /Supabase/src/mingwX64Test/kotlin/PlatformTargetTest.kt: -------------------------------------------------------------------------------- 1 | import io.github.jan.supabase.CurrentPlatformTarget 2 | import io.github.jan.supabase.PlatformTarget 3 | import kotlin.test.Test 4 | import kotlin.test.assertEquals 5 | 6 | actual class PlatformTargetTest { 7 | 8 | @Test 9 | actual fun testPlatformTarget() { 10 | assertEquals(PlatformTarget.WINDOWS, CurrentPlatformTarget, "The current platform target should be WINDOWS") 11 | } 12 | 13 | } -------------------------------------------------------------------------------- /Supabase/src/tvosMain/kotlin/io/github/jan/supabase/DefaultDispatcher.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase 2 | 3 | import kotlinx.coroutines.Dispatchers 4 | import kotlinx.coroutines.IO 5 | 6 | actual val defaultDispatcher = Dispatchers.IO -------------------------------------------------------------------------------- /Supabase/src/tvosMain/kotlin/io/github/jan/supabase/PlatformTarget.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase 2 | 3 | /** 4 | * The current target platform 5 | */ 6 | actual val CurrentPlatformTarget: PlatformTarget = PlatformTarget.TVOS -------------------------------------------------------------------------------- /Supabase/src/tvosMain/kotlin/io/github/jan/supabase/PlatformTarget.tvos.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase 2 | 3 | import io.supabase.supabase.getOSVersion 4 | 5 | internal actual fun getOSInformation(): OSInformation? = OSInformation( 6 | name = "tvOS", 7 | version = getOSVersion() 8 | ) -------------------------------------------------------------------------------- /Supabase/src/tvosTest/kotlin/PlatformTargetTest.kt: -------------------------------------------------------------------------------- 1 | import io.github.jan.supabase.CurrentPlatformTarget 2 | import io.github.jan.supabase.PlatformTarget 3 | import kotlin.test.Test 4 | import kotlin.test.assertEquals 5 | 6 | actual class PlatformTargetTest { 7 | 8 | @Test 9 | actual fun testPlatformTarget() { 10 | assertEquals(PlatformTarget.TVOS, CurrentPlatformTarget, "The current platform target should be TVOS") 11 | } 12 | 13 | } -------------------------------------------------------------------------------- /Supabase/src/wasmJsMain/kotlin/io/github/jan/supabase/DefaultDispatcher.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase 2 | 3 | import kotlinx.coroutines.Dispatchers 4 | 5 | actual val defaultDispatcher = Dispatchers.Default -------------------------------------------------------------------------------- /Supabase/src/wasmJsMain/kotlin/io/github/jan/supabase/PlatformTarget.wasmJs.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase 2 | 3 | /** 4 | * The current target platform 5 | */ 6 | actual val CurrentPlatformTarget: PlatformTarget = PlatformTarget.WASM_JS 7 | 8 | internal actual fun getOSInformation(): OSInformation? = null -------------------------------------------------------------------------------- /Supabase/src/wasmJsTest/kotlin/PlatformTargetTest.kt: -------------------------------------------------------------------------------- 1 | import io.github.jan.supabase.CurrentPlatformTarget 2 | import io.github.jan.supabase.PlatformTarget 3 | import kotlin.test.Test 4 | import kotlin.test.assertEquals 5 | 6 | actual class PlatformTargetTest { 7 | 8 | @Test 9 | actual fun testPlatformTarget() { 10 | assertEquals(PlatformTarget.WASM_JS, CurrentPlatformTarget, "The current platform target should be WASM_JS") 11 | } 12 | 13 | } -------------------------------------------------------------------------------- /Supabase/src/watchosMain/kotlin/io/github/jan/supabase/DefaultDispatcher.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase 2 | 3 | import kotlinx.coroutines.Dispatchers 4 | import kotlinx.coroutines.IO 5 | 6 | actual val defaultDispatcher = Dispatchers.IO -------------------------------------------------------------------------------- /Supabase/src/watchosMain/kotlin/io/github/jan/supabase/PlatformTarget.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase 2 | 3 | /** 4 | * The current target platform 5 | */ 6 | actual val CurrentPlatformTarget: PlatformTarget = PlatformTarget.WATCHOS -------------------------------------------------------------------------------- /Supabase/src/watchosMain/kotlin/io/github/jan/supabase/PlatformTarget.watchos.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase 2 | 3 | import io.supabase.supabase.getOSVersion 4 | 5 | internal actual fun getOSInformation(): OSInformation? = OSInformation( 6 | name = "tvOS", 7 | version = getOSVersion() 8 | ) -------------------------------------------------------------------------------- /Supabase/src/watchosTest/kotlin/PlatformTargetTest.kt: -------------------------------------------------------------------------------- 1 | import io.github.jan.supabase.CurrentPlatformTarget 2 | import io.github.jan.supabase.PlatformTarget 3 | import kotlin.test.Test 4 | import kotlin.test.assertEquals 5 | 6 | actual class PlatformTargetTest { 7 | 8 | @Test 9 | actual fun testPlatformTarget() { 10 | assertEquals(PlatformTarget.WATCHOS, CurrentPlatformTarget, "The current platform target should be WATCHOS") 11 | } 12 | 13 | } -------------------------------------------------------------------------------- /bom/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | `java-platform` 3 | } 4 | 5 | description = "A Kotlin Multiplatform Supabase Framework" 6 | 7 | val bomProject = project 8 | 9 | val excludedModules = listOf("test-common") 10 | 11 | fun shouldIncludeInBom(candidateProject: Project) = 12 | excludedModules.all { !candidateProject.name.contains(it) } && 13 | candidateProject.name != bomProject.name 14 | 15 | rootProject.subprojects.filter(::shouldIncludeInBom).forEach { bomProject.evaluationDependsOn(it.path) } 16 | 17 | dependencies { 18 | constraints { 19 | rootProject.subprojects.filter { project -> 20 | // Only declare dependencies on projects that will have publications 21 | shouldIncludeInBom(project) && project.tasks.findByName("publish")?.enabled == true 22 | }.forEach { api(project(it.path)) } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /buildSrc/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | `kotlin-dsl` 3 | } 4 | 5 | repositories { 6 | gradlePluginPortal() 7 | mavenCentral() 8 | google() 9 | } 10 | 11 | dependencies { 12 | implementation(libs.kotlin.multiplatform.gradle) 13 | implementation(libs.android.gradle.plugin) 14 | implementation(libs.detekt.gradle) 15 | implementation(libs.dokka.gradle) 16 | implementation(libs.publishing.gradle) 17 | implementation(libs.compose.gradle) 18 | implementation(libs.power.assert.gradle) 19 | } -------------------------------------------------------------------------------- /buildSrc/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | dependencyResolutionManagement { 2 | versionCatalogs { 3 | create("libs") { 4 | from(files("../gradle/libs.versions.toml")) 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/KotlinPlugin.kt: -------------------------------------------------------------------------------- 1 | import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi 2 | import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension 3 | 4 | @OptIn(ExperimentalKotlinGradlePluginApi::class) 5 | fun KotlinMultiplatformExtension.defaultConfig() { 6 | sourceSets.all { 7 | languageSettings.optIn("kotlin.RequiresOptIn") 8 | languageSettings.optIn("io.github.jan.supabase.annotations.SupabaseInternal") 9 | languageSettings.optIn("io.github.jan.supabase.annotations.SupabaseExperimental") 10 | } 11 | compilerOptions.freeCompilerArgs.add("-Xexpect-actual-classes") 12 | applyDefaultHierarchyTemplate() 13 | // jvmToolchain(8) 14 | } -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/PowerAssert.kt: -------------------------------------------------------------------------------- 1 | import org.gradle.api.Project 2 | import org.gradle.kotlin.dsl.apply 3 | import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi 4 | import org.jetbrains.kotlin.powerassert.gradle.PowerAssertGradleExtension 5 | 6 | @OptIn(ExperimentalKotlinGradlePluginApi::class) 7 | fun Project.applyPowerAssertConfiguration() { 8 | apply(plugin = "org.jetbrains.kotlin.plugin.power-assert") 9 | 10 | extensions.configure(PowerAssertGradleExtension::class.java) { 11 | functions.addAll( 12 | listOf("kotlin.assert", "kotlin.test.assertTrue", "kotlin.test.assertEquals", 13 | "kotlin.test.assertNull", "kotlin.test.assertIs", "kotlin.test.assertContentContains", 14 | "kotlin.test.assertContains") 15 | ) 16 | } 17 | } -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/TargetHierarchy.kt: -------------------------------------------------------------------------------- 1 | @file:OptIn(ExperimentalKotlinGradlePluginApi::class) 2 | 3 | import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi 4 | import org.jetbrains.kotlin.gradle.plugin.KotlinHierarchyBuilder 5 | 6 | fun KotlinHierarchyBuilder.androidAndJvmGroup() { 7 | group("androidAndJvm") { 8 | withJvm() 9 | withAndroidTarget() 10 | } 11 | } 12 | 13 | fun KotlinHierarchyBuilder.settingsGroup() { 14 | group("settings") { 15 | withJvm() 16 | withAndroidTarget() 17 | withJs() 18 | withMingwX64() 19 | withApple() 20 | withWasmJs() 21 | } 22 | } -------------------------------------------------------------------------------- /demos/android-login/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | !**/src/main/**/build/ 5 | !**/src/test/**/build/ 6 | 7 | ### IntelliJ IDEA ### 8 | .idea/modules.xml 9 | .idea/jarRepositories.xml 10 | .idea/compiler.xml 11 | .idea/libraries/ 12 | *.iws 13 | *.iml 14 | *.ipr 15 | out/ 16 | !**/src/main/**/out/ 17 | !**/src/test/**/out/ 18 | 19 | ### Eclipse ### 20 | .apt_generated 21 | .classpath 22 | .factorypath 23 | .project 24 | .settings 25 | .springBeans 26 | .sts4-cache 27 | bin/ 28 | !**/src/main/**/bin/ 29 | !**/src/test/**/bin/ 30 | 31 | ### NetBeans ### 32 | /nbproject/private/ 33 | /nbbuild/ 34 | /dist/ 35 | /nbdist/ 36 | /.nb-gradle/ 37 | 38 | ### VS Code ### 39 | .vscode/ 40 | 41 | ### Mac OS ### 42 | .DS_Store 43 | 44 | local.properties 45 | 46 | /wix311/ -------------------------------------------------------------------------------- /demos/android-login/android/src/main/java/io/github/jan/supabase/android/MainApplication.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.android 2 | 3 | import android.app.Application 4 | import io.github.jan.supabase.common.di.initKoin 5 | 6 | class MainApplication: Application() { 7 | 8 | override fun onCreate() { 9 | super.onCreate() 10 | initKoin() 11 | } 12 | 13 | } -------------------------------------------------------------------------------- /demos/android-login/build.gradle.kts: -------------------------------------------------------------------------------- 1 | group = "io.github.jan.supabase" 2 | version = "1.0-SNAPSHOT" 3 | 4 | plugins { 5 | alias(libs.plugins.kotlin.multiplatform) apply false 6 | alias(libs.plugins.kotlin.android) apply false 7 | alias(libs.plugins.kotlinx.plugin.serialization) apply false 8 | alias(libs.plugins.android.application) apply false 9 | alias(libs.plugins.android.library) apply false 10 | alias(libs.plugins.compose) apply false 11 | } 12 | 13 | allprojects { 14 | repositories { 15 | google() 16 | mavenCentral() 17 | maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") 18 | } 19 | } 20 | 21 | -------------------------------------------------------------------------------- /demos/android-login/buildSrc/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | `kotlin-dsl` 3 | } 4 | 5 | repositories { 6 | mavenCentral() 7 | } 8 | 9 | 10 | -------------------------------------------------------------------------------- /demos/android-login/buildSrc/src/main/kotlin/Versions.kt: -------------------------------------------------------------------------------- 1 | object Versions { 2 | 3 | const val KOIN = "3.3.3" 4 | const val SUPABASE = "0.8.4" 5 | const val LIFECYCLE = "2.6.0" 6 | const val KTOR = "2.2.4" 7 | 8 | } -------------------------------------------------------------------------------- /demos/android-login/common/src/androidMain/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /demos/android-login/common/src/androidMain/kotlin/io/github/jan/supabase/common/MPViewModel.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common 2 | 3 | import androidx.lifecycle.ViewModel 4 | import androidx.lifecycle.viewModelScope 5 | 6 | actual open class MPViewModel actual constructor(): ViewModel() { 7 | 8 | actual val coroutineScope = viewModelScope 9 | 10 | } -------------------------------------------------------------------------------- /demos/android-login/common/src/androidMain/kotlin/io/github/jan/supabase/common/di/platformGoTrueConfig.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common.di 2 | 3 | import io.github.jan.supabase.gotrue.GoTrueConfig 4 | 5 | actual fun GoTrueConfig.platformGoTrueConfig() { 6 | scheme = "io.jan.supabase" 7 | host = "login" 8 | } -------------------------------------------------------------------------------- /demos/android-login/common/src/androidMain/kotlin/io/github/jan/supabase/common/di/viewModel.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common.di 2 | 3 | import org.koin.core.module.Module 4 | 5 | actual fun Module.viewModel() { 6 | viewModel { createViewModule() } 7 | } -------------------------------------------------------------------------------- /demos/android-login/common/src/commonMain/kotlin/io/github/jan/supabase/common/di/koin.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common.di 2 | 3 | import org.koin.core.KoinApplication 4 | import org.koin.core.context.startKoin 5 | 6 | fun initKoin(additionalConfiguration: KoinApplication.() -> Unit = {}) { 7 | startKoin { 8 | modules(supabaseModule, viewModelModule) 9 | additionalConfiguration() 10 | } 11 | } -------------------------------------------------------------------------------- /demos/android-login/common/src/commonMain/kotlin/io/github/jan/supabase/common/di/viewModelModule.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common.di 2 | 3 | import io.github.jan.supabase.common.AppViewModel 4 | import org.koin.core.module.Module 5 | import org.koin.core.scope.Scope 6 | import org.koin.dsl.module 7 | 8 | expect fun Module.viewModel() 9 | 10 | fun Scope.createViewModule() = AppViewModel(get()) 11 | 12 | val viewModelModule = module { 13 | viewModel() 14 | } -------------------------------------------------------------------------------- /demos/android-login/gradle.properties: -------------------------------------------------------------------------------- 1 | kotlin.code.style=official 2 | kotlin.native.enableDependencyPropagation=false 3 | android.useAndroidX=true 4 | org.jetbrains.compose.experimental.jscanvas.enabled=true 5 | org.gradle.jvmargs=-Xmx4096m 6 | kotlin.mpp.androidSourceSetLayoutVersion=2 -------------------------------------------------------------------------------- /demos/android-login/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supabase-community/supabase-kt/8d9207594a4f651f3f8ff2e4a5082787d7f15deb/demos/android-login/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /demos/android-login/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip 4 | networkTimeout=10000 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /demos/android-login/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | // Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. 2 | pluginManagement { 3 | repositories { 4 | google() 5 | gradlePluginPortal() 6 | mavenCentral() 7 | maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") 8 | } 9 | } 10 | 11 | rootProject.name = "android-login" 12 | 13 | include(":android", ":desktop", ":web", ":common") 14 | -------------------------------------------------------------------------------- /demos/multiplatform-deeplinks/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | build/ 3 | desktop/output 4 | !gradle/wrapper/gradle-wrapper.jar 5 | !**/src/main/**/build/ 6 | !**/src/test/**/build/ 7 | 8 | ### IntelliJ IDEA ### 9 | .idea/modules.xml 10 | .idea/jarRepositories.xml 11 | .idea/compiler.xml 12 | .idea/libraries/ 13 | *.iws 14 | *.iml 15 | *.ipr 16 | out/ 17 | !**/src/main/**/out/ 18 | !**/src/test/**/out/ 19 | 20 | ### Eclipse ### 21 | .apt_generated 22 | .classpath 23 | .factorypath 24 | .project 25 | .settings 26 | .springBeans 27 | .sts4-cache 28 | bin/ 29 | !**/src/main/**/bin/ 30 | !**/src/test/**/bin/ 31 | 32 | ### NetBeans ### 33 | /nbproject/private/ 34 | /nbbuild/ 35 | /dist/ 36 | /nbdist/ 37 | /.nb-gradle/ 38 | 39 | ### VS Code ### 40 | .vscode/ 41 | 42 | ### Mac OS ### 43 | .DS_Store 44 | 45 | local.properties 46 | 47 | /wix311/ -------------------------------------------------------------------------------- /demos/multiplatform-deeplinks/android/src/main/java/io/github/jan/supabase/android/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.android 2 | 3 | import android.os.Bundle 4 | import androidx.activity.ComponentActivity 5 | import androidx.activity.compose.setContent 6 | import androidx.compose.material3.MaterialTheme 7 | 8 | import co.touchlab.kermit.Logger 9 | import io.github.jan.supabase.common.App 10 | import io.github.jan.supabase.common.AppViewModel 11 | import io.github.jan.supabase.gotrue.handleDeeplinks 12 | import org.koin.android.ext.android.inject 13 | 14 | class MainActivity : ComponentActivity() { 15 | 16 | private val viewModel: AppViewModel by inject() 17 | 18 | override fun onCreate(savedInstanceState: Bundle?) { 19 | super.onCreate(savedInstanceState) 20 | Logger.base(DebugAntilog()) 21 | viewModel.supabaseClient.handleDeeplinks(intent) 22 | setContent { 23 | MaterialTheme { 24 | App(viewModel) 25 | } 26 | } 27 | } 28 | 29 | } -------------------------------------------------------------------------------- /demos/multiplatform-deeplinks/android/src/main/java/io/github/jan/supabase/android/MainApplication.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.android 2 | 3 | import android.app.Application 4 | import io.github.jan.supabase.common.di.initKoin 5 | 6 | class MainApplication: Application() { 7 | 8 | override fun onCreate() { 9 | super.onCreate() 10 | initKoin() 11 | } 12 | 13 | } -------------------------------------------------------------------------------- /demos/multiplatform-deeplinks/build.gradle.kts: -------------------------------------------------------------------------------- 1 | group = "io.github.jan.supabase" 2 | version = "1.0-SNAPSHOT" 3 | 4 | allprojects { 5 | repositories { 6 | google() 7 | mavenCentral() 8 | maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") 9 | mavenLocal() 10 | } 11 | } 12 | 13 | plugins { 14 | alias(libs.plugins.kotlin.multiplatform) apply false 15 | alias(libs.plugins.kotlin.android) apply false 16 | alias(libs.plugins.kotlinx.plugin.serialization) apply false 17 | alias(libs.plugins.android.application) apply false 18 | alias(libs.plugins.android.library) apply false 19 | alias(libs.plugins.compose) apply false 20 | } -------------------------------------------------------------------------------- /demos/multiplatform-deeplinks/buildSrc/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | `kotlin-dsl` 3 | } 4 | 5 | repositories { 6 | mavenCentral() 7 | } 8 | 9 | 10 | -------------------------------------------------------------------------------- /demos/multiplatform-deeplinks/buildSrc/src/main/kotlin/Versions.kt: -------------------------------------------------------------------------------- 1 | object Versions { 2 | 3 | const val KOIN = "3.3.3" 4 | const val SUPABASE = "0.8.4" 5 | const val LIFECYCLE = "2.6.0" 6 | const val KTOR = "2.2.4" 7 | 8 | } -------------------------------------------------------------------------------- /demos/multiplatform-deeplinks/common/src/androidMain/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /demos/multiplatform-deeplinks/common/src/androidMain/kotlin/io/github/jan/supabase/common/MPViewModel.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common 2 | 3 | import androidx.lifecycle.ViewModel 4 | import androidx.lifecycle.viewModelScope 5 | 6 | actual open class MPViewModel actual constructor(): ViewModel() { 7 | 8 | actual val coroutineScope = viewModelScope 9 | 10 | } -------------------------------------------------------------------------------- /demos/multiplatform-deeplinks/common/src/androidMain/kotlin/io/github/jan/supabase/common/di/platformGoTrueConfig.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common.di 2 | 3 | import io.github.jan.supabase.gotrue.GoTrueConfig 4 | 5 | actual fun GoTrueConfig.platformGoTrueConfig() { 6 | scheme = "io.github.jan.supabase" 7 | host = "login" 8 | } -------------------------------------------------------------------------------- /demos/multiplatform-deeplinks/common/src/androidMain/kotlin/io/github/jan/supabase/common/di/viewModel.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common.di 2 | 3 | import org.koin.core.module.Module 4 | 5 | actual fun Module.viewModel() { 6 | viewModel { createViewModule() } 7 | } -------------------------------------------------------------------------------- /demos/multiplatform-deeplinks/common/src/androidMain/kotlin/io/github/jan/supabase/common/ui/components/AlertDialog.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common.ui.components 2 | 3 | import androidx.compose.material3.Button 4 | import androidx.compose.material3.Text 5 | import androidx.compose.runtime.Composable 6 | 7 | @Composable 8 | actual fun AlertDialog(text: String, close: () -> Unit) { 9 | androidx.compose.material3.AlertDialog( 10 | text = { Text(text) }, 11 | onDismissRequest = close, 12 | confirmButton = { Button(onClick = close) { Text("Ok") } } 13 | ) 14 | } -------------------------------------------------------------------------------- /demos/multiplatform-deeplinks/common/src/commonMain/kotlin/io/github/jan/supabase/common/di/koin.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common.di 2 | 3 | import org.koin.core.KoinApplication 4 | import org.koin.core.context.startKoin 5 | 6 | fun initKoin(additionalConfiguration: KoinApplication.() -> Unit = {}) { 7 | startKoin { 8 | modules(supabaseModule, netModule, viewModelModule) 9 | additionalConfiguration() 10 | } 11 | } -------------------------------------------------------------------------------- /demos/multiplatform-deeplinks/common/src/commonMain/kotlin/io/github/jan/supabase/common/di/netModule.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common.di 2 | 3 | import org.koin.dsl.module 4 | 5 | val netModule = module { 6 | 7 | } -------------------------------------------------------------------------------- /demos/multiplatform-deeplinks/common/src/commonMain/kotlin/io/github/jan/supabase/common/di/supabaseModule.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common.di 2 | 3 | import io.github.jan.supabase.createSupabaseClient 4 | import io.github.jan.supabase.gotrue.GoTrue 5 | import io.github.jan.supabase.gotrue.GoTrueConfig 6 | import org.koin.dsl.module 7 | 8 | expect fun GoTrueConfig.platformGoTrueConfig() 9 | 10 | val supabaseModule = module { 11 | single { 12 | createSupabaseClient( 13 | supabaseUrl = "YOUR_URL", 14 | supabaseKey = "YOUR_KEY" 15 | ) { 16 | install(GoTrue) { 17 | platformGoTrueConfig() 18 | } 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /demos/multiplatform-deeplinks/common/src/commonMain/kotlin/io/github/jan/supabase/common/di/viewModelModule.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common.di 2 | 3 | import io.github.jan.supabase.common.AppViewModel 4 | import org.koin.core.module.Module 5 | import org.koin.core.scope.Scope 6 | import org.koin.dsl.module 7 | 8 | expect fun Module.viewModel() 9 | 10 | fun Scope.createViewModule() = AppViewModel(get()) 11 | 12 | val viewModelModule = module { 13 | viewModel() 14 | } -------------------------------------------------------------------------------- /demos/multiplatform-deeplinks/common/src/commonMain/kotlin/io/github/jan/supabase/common/ui/components/AlertDialog.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common.ui.components 2 | 3 | import androidx.compose.runtime.Composable 4 | 5 | @Composable 6 | expect fun AlertDialog(text: String, close: () -> Unit) -------------------------------------------------------------------------------- /demos/multiplatform-deeplinks/common/src/commonMain/kotlin/io/github/jan/supabase/common/ui/screen/AppScreen.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common.ui.screen 2 | 3 | import androidx.compose.foundation.layout.Arrangement 4 | import androidx.compose.foundation.layout.Column 5 | import androidx.compose.foundation.layout.fillMaxSize 6 | import androidx.compose.material3.Button 7 | import androidx.compose.material3.Text 8 | import androidx.compose.runtime.Composable 9 | import androidx.compose.ui.Alignment 10 | import androidx.compose.ui.Modifier 11 | import io.github.jan.supabase.common.AppViewModel 12 | 13 | @Composable 14 | fun AppScreen(viewModel: AppViewModel) { 15 | Column( 16 | modifier = Modifier.fillMaxSize(), 17 | verticalArrangement = Arrangement.Center, 18 | horizontalAlignment = Alignment.CenterHorizontally 19 | ) { 20 | Text("Logged in!") 21 | Button( 22 | onClick = { viewModel.logout() } 23 | ) { 24 | Text("Logout") 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /demos/multiplatform-deeplinks/common/src/desktopMain/kotlin/io/github/jan/supabase/common/MPViewModel.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common 2 | 3 | import kotlinx.coroutines.CoroutineScope 4 | import kotlinx.coroutines.Dispatchers 5 | 6 | actual open class MPViewModel actual constructor() { 7 | 8 | actual val coroutineScope = CoroutineScope(Dispatchers.Default) 9 | 10 | } -------------------------------------------------------------------------------- /demos/multiplatform-deeplinks/common/src/desktopMain/kotlin/io/github/jan/supabase/common/di/platformGoTrueConfig.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common.di 2 | 3 | import io.github.jan.supabase.gotrue.GoTrueConfig 4 | 5 | actual fun GoTrueConfig.platformGoTrueConfig() { 6 | htmlTitle = "Chat App" 7 | } -------------------------------------------------------------------------------- /demos/multiplatform-deeplinks/common/src/desktopMain/kotlin/io/github/jan/supabase/common/di/viewModel.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common.di 2 | 3 | import org.koin.core.module.Module 4 | 5 | actual fun Module.viewModel() { 6 | single { createViewModule() } 7 | } -------------------------------------------------------------------------------- /demos/multiplatform-deeplinks/gradle.properties: -------------------------------------------------------------------------------- 1 | kotlin.code.style=official 2 | kotlin.native.enableDependencyPropagation=false 3 | android.useAndroidX=true 4 | org.jetbrains.compose.experimental.jscanvas.enabled=true 5 | org.gradle.jvmargs=-Xmx4096m 6 | kotlin.mpp.androidSourceSetLayoutVersion=2 -------------------------------------------------------------------------------- /demos/multiplatform-deeplinks/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supabase-community/supabase-kt/8d9207594a4f651f3f8ff2e4a5082787d7f15deb/demos/multiplatform-deeplinks/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /demos/multiplatform-deeplinks/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip 4 | networkTimeout=10000 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /demos/multiplatform-deeplinks/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | // Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. 2 | pluginManagement { 3 | repositories { 4 | google() 5 | gradlePluginPortal() 6 | mavenCentral() 7 | maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") 8 | maven("https://maven.hq.hydraulic.software") 9 | } 10 | } 11 | 12 | rootProject.name = "multiplatform-deeplinks" 13 | 14 | include(":desktop", ":common", ":android") 15 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | kotlin.code.style=official 2 | android.useAndroidX=true 3 | org.gradle.jvmargs=-Xmx4096m 4 | kotlin.mpp.androidSourceSetLayoutVersion=2 5 | kotlin.native.ignoreDisabledTargets=true 6 | org.gradle.parallel=true 7 | kotlin.suppressGradlePluginWarnings=IncorrectCompileOnlyDependencyWarning 8 | 9 | org.jetbrains.compose.experimental.uikit.enabled=true 10 | org.jetbrains.compose.experimental.jscanvas.enabled=true 11 | org.jetbrains.compose.experimental.wasm.enabled=true 12 | org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled 13 | 14 | supabase-version = 3.2.0-beta-2 15 | base-group = io.github.jan-tennert.supabase 16 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supabase-community/supabase-kt/8d9207594a4f651f3f8ff2e4a5082787d7f15deb/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /plugins/ApolloGraphQL/src/androidMain/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /plugins/Coil3Integration/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id(libs.plugins.kotlin.multiplatform.get().pluginId) 3 | id(libs.plugins.android.library.get().pluginId) 4 | } 5 | 6 | description = "Extends supabase-kt with a Coil3 integration for easy image loading" 7 | 8 | repositories { 9 | mavenCentral() 10 | maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") 11 | } 12 | 13 | @OptIn(org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi::class) 14 | kotlin { 15 | defaultConfig() 16 | composeTargets() 17 | sourceSets { 18 | val commonMain by getting { 19 | dependencies { 20 | api(project(":storage-kt")) 21 | api(libs.bundles.coil3) 22 | } 23 | } 24 | } 25 | } 26 | 27 | configureLibraryAndroidTarget() -------------------------------------------------------------------------------- /plugins/Coil3Integration/src/androidMain/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /plugins/CoilIntegration/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id(libs.plugins.kotlin.multiplatform.get().pluginId) 3 | id(libs.plugins.android.library.get().pluginId) 4 | } 5 | 6 | description = "Extends supabase-kt with a Coil integration for easy image loading" 7 | 8 | repositories { 9 | mavenCentral() 10 | maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") 11 | } 12 | 13 | @OptIn(org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi::class) 14 | kotlin { 15 | defaultConfig() 16 | configuredAndroidTarget() 17 | sourceSets { 18 | val commonMain by getting { 19 | dependencies { 20 | api(project(":storage-kt")) 21 | api(libs.coil2) 22 | } 23 | } 24 | } 25 | } 26 | 27 | configureLibraryAndroidTarget() -------------------------------------------------------------------------------- /plugins/CoilIntegration/src/androidMain/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /plugins/ComposeAuth/src/androidMain/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 11 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /plugins/ComposeAuth/src/androidMain/kotlin/io/github/jan/supabase/compose/auth/SupabaseInitializer.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.compose.auth 2 | 3 | import android.content.Context 4 | import androidx.startup.Initializer 5 | 6 | private var appContext: Context? = null 7 | 8 | internal class SupabaseInitializer : Initializer { 9 | override fun create(context: Context): Context = context.applicationContext.also { appContext = it } 10 | 11 | override fun dependencies(): List>> = emptyList() 12 | 13 | } 14 | 15 | internal fun applicationContext(): Context = appContext ?: error("Application context not initialized") -------------------------------------------------------------------------------- /plugins/ComposeAuth/src/androidMain/kotlin/io/github/jan/supabase/compose/auth/composable/AppleAuth.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.compose.auth.composable 2 | 3 | import androidx.compose.runtime.Composable 4 | import io.github.jan.supabase.compose.auth.ComposeAuth 5 | import io.github.jan.supabase.compose.auth.defaultLoginBehavior 6 | 7 | /** 8 | * Composable function that implements Native Apple Auth. 9 | * 10 | * On unsupported platforms it will use the [fallback] 11 | * 12 | * @param onResult Callback for the result of the login 13 | * @param fallback Fallback function for unsupported platforms 14 | * @return [NativeSignInState] 15 | */ 16 | @Composable 17 | actual fun ComposeAuth.rememberSignInWithApple(onResult: (NativeSignInResult) -> Unit, fallback: suspend () -> Unit): NativeSignInState = defaultLoginBehavior(fallback) -------------------------------------------------------------------------------- /plugins/ComposeAuth/src/appleMain/kotlin/io/github/jan/supabase/compose/auth/composable/GoogleAuth.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.compose.auth.composable 2 | 3 | import androidx.compose.runtime.Composable 4 | import io.github.jan.supabase.compose.auth.ComposeAuth 5 | import io.github.jan.supabase.compose.auth.defaultLoginBehavior 6 | 7 | /** 8 | * Composable function that implements Native Google Auth. 9 | * 10 | * On unsupported platforms it will use the [fallback] 11 | * 12 | * @param onResult Callback for the result of the login 13 | * @param fallback Fallback function for unsupported platforms 14 | * @return [NativeSignInState] 15 | */ 16 | @Composable 17 | actual fun ComposeAuth.rememberSignInWithGoogle( 18 | onResult: (NativeSignInResult) -> Unit, 19 | type: GoogleDialogType, 20 | fallback: suspend () -> Unit 21 | ): NativeSignInState = defaultLoginBehavior(fallback) 22 | 23 | internal actual suspend fun handleGoogleSignOut() = Unit -------------------------------------------------------------------------------- /plugins/ComposeAuth/src/commonMain/kotlin/io/github/jan/supabase/compose/auth/AppleLoginConfig.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.compose.auth 2 | 3 | /** 4 | * Config for Apple's Authorization API 5 | * 6 | * Note: This is a placeholder for future implementation 7 | */ 8 | data object AppleLoginConfig 9 | 10 | /** 11 | * Helper function that return native configs 12 | */ 13 | fun ComposeAuth.Config.appleNativeLogin() { 14 | appleLoginConfig = AppleLoginConfig 15 | } -------------------------------------------------------------------------------- /plugins/ComposeAuth/src/commonMain/kotlin/io/github/jan/supabase/compose/auth/Utils.common.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.compose.auth 2 | 3 | import okio.ByteString.Companion.toByteString 4 | 5 | internal fun String.hash(): String { 6 | val hash = this.encodeToByteArray().toByteString() 7 | return hash.sha256().hex() 8 | } -------------------------------------------------------------------------------- /plugins/ComposeAuth/src/commonMain/kotlin/io/github/jan/supabase/compose/auth/composable/NativeAppleAuth.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.compose.auth.composable 2 | 3 | import androidx.compose.runtime.Composable 4 | import io.github.jan.supabase.auth.providers.Apple 5 | import io.github.jan.supabase.compose.auth.ComposeAuth 6 | import io.github.jan.supabase.compose.auth.fallbackLogin 7 | 8 | /** 9 | * Composable function that implements Native Apple Auth. 10 | * 11 | * On unsupported platforms it will use the [fallback] 12 | * 13 | * @param onResult Callback for the result of the login 14 | * @param fallback Fallback function for unsupported platforms 15 | * @return [NativeSignInState] 16 | */ 17 | @Composable 18 | expect fun ComposeAuth.rememberSignInWithApple(onResult: (NativeSignInResult) -> Unit, fallback: suspend () -> Unit = { fallbackLogin(Apple) }) : NativeSignInState -------------------------------------------------------------------------------- /plugins/ComposeAuth/src/noDefaultMain/kotlin/io/github/jan/supabase/compose/auth/composable/AppleAuth.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.compose.auth.composable 2 | 3 | import androidx.compose.runtime.Composable 4 | import io.github.jan.supabase.compose.auth.ComposeAuth 5 | import io.github.jan.supabase.compose.auth.defaultLoginBehavior 6 | 7 | /** 8 | * Composable for Apple login with default behavior 9 | */ 10 | @Composable 11 | actual fun ComposeAuth.rememberSignInWithApple(onResult: (NativeSignInResult) -> Unit, fallback: suspend () -> Unit): NativeSignInState = defaultLoginBehavior(fallback) -------------------------------------------------------------------------------- /plugins/ComposeAuth/src/noDefaultMain/kotlin/io/github/jan/supabase/compose/auth/composable/GoogleAuth.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.compose.auth.composable 2 | 3 | import androidx.compose.runtime.Composable 4 | import io.github.jan.supabase.compose.auth.ComposeAuth 5 | import io.github.jan.supabase.compose.auth.defaultLoginBehavior 6 | 7 | /** 8 | * Composable function that implements Native Google Auth. 9 | * 10 | * On unsupported platforms it will use the [fallback] 11 | * 12 | * @param onResult Callback for the result of the login 13 | * @param fallback Fallback function for unsupported platforms 14 | * @return [NativeSignInState] 15 | */ 16 | @Composable 17 | actual fun ComposeAuth.rememberSignInWithGoogle( 18 | onResult: (NativeSignInResult) -> Unit, 19 | type: GoogleDialogType, 20 | fallback: suspend () -> Unit 21 | ): NativeSignInState = defaultLoginBehavior(fallback) 22 | 23 | internal actual suspend fun handleGoogleSignOut() = Unit -------------------------------------------------------------------------------- /plugins/ComposeAuthUI/src/androidMain/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /plugins/ComposeAuthUI/src/commonMain/kotlin/io/github/jan/supabase/compose/auth/ui/FormValidator.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.compose.auth.ui 2 | 3 | /** 4 | * FormValidator interface used to validate text fields. 5 | */ 6 | fun interface FormValidator { 7 | 8 | /** 9 | * Validates the given [value]. 10 | */ 11 | fun validate(value: String): Boolean 12 | 13 | companion object { 14 | 15 | private val emailRegex = Regex("^[\\w\\-\\.]+@([\\w-]+\\.)+[\\w-]{2,}\$") 16 | 17 | /** 18 | * Validates the given [value] as an email, using REGEX. 19 | */ 20 | val EMAIL = FormValidator { 21 | emailRegex.matches(it) 22 | } 23 | 24 | /** 25 | * Validates the given [value] as a phone number, by verifying that all characters are digits. 26 | */ 27 | val PHONE = FormValidator { 28 | it.all { char -> char.isDigit() } 29 | } 30 | 31 | } 32 | 33 | } -------------------------------------------------------------------------------- /plugins/ComposeAuthUI/src/commonMain/kotlin/io/github/jan/supabase/compose/auth/ui/annotations/AuthUiExperimental.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.compose.auth.ui.annotations 2 | 3 | /** 4 | * Used to mark experimental Compose Auth Ui APIs 5 | */ 6 | @RequiresOptIn(level = RequiresOptIn.Level.ERROR, message = "This API is experimental and may not be stable yet") 7 | annotation class AuthUiExperimental -------------------------------------------------------------------------------- /plugins/ComposeAuthUI/src/jvmMain/kotlin/io/github/jan/supabase/compose/auth/ui/SvgPainterJvm.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.compose.auth.ui 2 | 3 | import androidx.compose.ui.graphics.painter.Painter 4 | import androidx.compose.ui.res.loadSvgPainter 5 | import androidx.compose.ui.unit.Density 6 | import io.github.jan.supabase.annotations.SupabaseInternal 7 | 8 | @SupabaseInternal 9 | actual fun svgPainter(bytes: ByteArray, density: Density): Painter = loadSvgPainter(bytes.inputStream(), density) -------------------------------------------------------------------------------- /plugins/ImageLoaderIntegration/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id(libs.plugins.kotlin.multiplatform.get().pluginId) 3 | id(libs.plugins.android.library.get().pluginId) 4 | } 5 | 6 | description = "Extends supabase-kt with a Compose-ImageLoader integration for easy image loading" 7 | 8 | repositories { 9 | mavenCentral() 10 | maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") 11 | } 12 | 13 | @OptIn(org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi::class) 14 | kotlin { 15 | defaultConfig() 16 | // composeTargets() 17 | jsTarget() 18 | jvmTargets() 19 | iosTargets() 20 | sourceSets { 21 | val commonMain by getting { 22 | dependencies { 23 | api(project(":storage-kt")) 24 | api(libs.imageloader) 25 | implementation(libs.okio) 26 | } 27 | } 28 | } 29 | } 30 | 31 | configureLibraryAndroidTarget() -------------------------------------------------------------------------------- /plugins/ImageLoaderIntegration/src/androidMain/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /sample/chat-demo-mpp/android/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id(libs.plugins.compose.plugin.get().pluginId) 3 | alias(libs.plugins.compose.compiler) 4 | id("com.android.application") 5 | id(libs.plugins.kotlin.android.get().pluginId) 6 | alias(libs.plugins.kotlinx.plugin.serialization) 7 | } 8 | 9 | group = "io.github.jan.supabase" 10 | version = "1.0-SNAPSHOT" 11 | 12 | dependencies { 13 | implementation(project(":sample:chat-demo-mpp:common")) 14 | implementation(libs.androidx.activity.compose) 15 | } 16 | 17 | android { 18 | configureApplicationAndroidTarget(JavaVersion.VERSION_11) 19 | kotlinOptions { 20 | jvmTarget = "11" 21 | } 22 | } -------------------------------------------------------------------------------- /sample/chat-demo-mpp/android/src/main/java/io/github/jan/supabase/android/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.android 2 | 3 | import android.os.Bundle 4 | import androidx.activity.ComponentActivity 5 | import androidx.activity.compose.setContent 6 | import androidx.compose.material3.MaterialTheme 7 | import io.github.jan.supabase.auth.handleDeeplinks 8 | import io.github.jan.supabase.common.App 9 | import io.github.jan.supabase.common.ChatViewModel 10 | import org.koin.android.ext.android.inject 11 | 12 | class MainActivity : ComponentActivity() { 13 | 14 | private val viewModel: ChatViewModel by inject() 15 | 16 | override fun onCreate(savedInstanceState: Bundle?) { 17 | super.onCreate(savedInstanceState) 18 | viewModel.supabaseClient.handleDeeplinks(intent) 19 | setContent { 20 | MaterialTheme { 21 | App(viewModel) 22 | } 23 | } 24 | } 25 | 26 | } -------------------------------------------------------------------------------- /sample/chat-demo-mpp/android/src/main/java/io/github/jan/supabase/android/MainApplication.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.android 2 | 3 | import android.app.Application 4 | import io.github.jan.supabase.common.di.initKoin 5 | 6 | class MainApplication: Application() { 7 | 8 | override fun onCreate() { 9 | super.onCreate() 10 | initKoin() 11 | } 12 | 13 | } -------------------------------------------------------------------------------- /sample/chat-demo-mpp/chatdemoios/chatdemoios.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /sample/chat-demo-mpp/common/src/androidMain/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /sample/chat-demo-mpp/common/src/androidMain/kotlin/io/github/jan/supabase/common/MPViewModel.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common 2 | 3 | import androidx.lifecycle.ViewModel 4 | import androidx.lifecycle.viewModelScope 5 | 6 | actual open class MPViewModel actual constructor(): ViewModel() { 7 | 8 | actual val coroutineScope = viewModelScope 9 | 10 | } -------------------------------------------------------------------------------- /sample/chat-demo-mpp/common/src/androidMain/kotlin/io/github/jan/supabase/common/di/platformGoTrueConfig.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common.di 2 | 3 | import io.github.jan.supabase.auth.AuthConfig 4 | import io.github.jan.supabase.auth.ExternalAuthAction 5 | 6 | actual fun AuthConfig.platformGoTrueConfig() { 7 | scheme = "io.jan.supabase" 8 | host = "login" 9 | defaultExternalAuthAction = ExternalAuthAction.CustomTabs() 10 | } -------------------------------------------------------------------------------- /sample/chat-demo-mpp/common/src/androidMain/kotlin/io/github/jan/supabase/common/di/viewModel.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common.di 2 | 3 | import org.koin.androidx.viewmodel.dsl.viewModel 4 | import org.koin.core.module.Module 5 | 6 | actual fun Module.viewModel() { 7 | viewModel { createViewModule() } 8 | } -------------------------------------------------------------------------------- /sample/chat-demo-mpp/common/src/commonMain/kotlin/io/github/jan/supabase/common/di/koin.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common.di 2 | 3 | import org.koin.core.KoinApplication 4 | import org.koin.core.context.startKoin 5 | 6 | fun initKoin(additionalConfiguration: KoinApplication.() -> Unit = {}) { 7 | startKoin { 8 | modules(supabaseModule, netModule, viewModelModule) 9 | additionalConfiguration() 10 | } 11 | } -------------------------------------------------------------------------------- /sample/chat-demo-mpp/common/src/commonMain/kotlin/io/github/jan/supabase/common/di/netModule.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common.di 2 | 3 | import io.github.jan.supabase.common.net.AuthApi 4 | import io.github.jan.supabase.common.net.AuthApiImpl 5 | import io.github.jan.supabase.common.net.MessageApi 6 | import io.github.jan.supabase.common.net.MessageApiImpl 7 | import org.koin.dsl.module 8 | 9 | val netModule = module { 10 | single { MessageApiImpl(get()) } 11 | single { AuthApiImpl(get()) } 12 | } -------------------------------------------------------------------------------- /sample/chat-demo-mpp/common/src/commonMain/kotlin/io/github/jan/supabase/common/di/viewModelModule.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common.di 2 | 3 | import io.github.jan.supabase.common.ChatViewModel 4 | import org.koin.core.module.Module 5 | import org.koin.core.scope.Scope 6 | import org.koin.dsl.module 7 | 8 | expect fun Module.viewModel() 9 | 10 | fun Scope.createViewModule() = ChatViewModel(get(), get(), get()) 11 | 12 | val viewModelModule = module { 13 | viewModel() 14 | } -------------------------------------------------------------------------------- /sample/chat-demo-mpp/common/src/desktopMain/kotlin/io/github/jan/supabase/common/MPViewModel.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common 2 | 3 | import kotlinx.coroutines.CoroutineScope 4 | import kotlinx.coroutines.Dispatchers 5 | 6 | actual open class MPViewModel actual constructor() { 7 | 8 | actual val coroutineScope = CoroutineScope(Dispatchers.Default) 9 | 10 | } -------------------------------------------------------------------------------- /sample/chat-demo-mpp/common/src/desktopMain/kotlin/io/github/jan/supabase/common/di/platformGoTrueConfig.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common.di 2 | 3 | import io.github.jan.supabase.auth.AuthConfig 4 | 5 | actual fun AuthConfig.platformGoTrueConfig() { 6 | httpCallbackConfig { 7 | this.htmlTitle = "Chat App" 8 | } 9 | } -------------------------------------------------------------------------------- /sample/chat-demo-mpp/common/src/desktopMain/kotlin/io/github/jan/supabase/common/di/viewModel.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common.di 2 | 3 | import org.koin.core.module.Module 4 | 5 | actual fun Module.viewModel() { 6 | single { createViewModule() } 7 | } -------------------------------------------------------------------------------- /sample/chat-demo-mpp/common/src/iosMain/kotlin/io/github/jan/supabase/common/App.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common 2 | 3 | import androidx.compose.ui.window.ComposeUIViewController 4 | import org.koin.core.component.KoinComponent 5 | import org.koin.core.component.inject 6 | import platform.UIKit.UIViewController 7 | 8 | class RootComponent : KoinComponent { 9 | private val viewModel: ChatViewModel by inject() 10 | fun getViewModel(): ChatViewModel = viewModel 11 | } 12 | 13 | fun AppIos(viewModel: ChatViewModel): UIViewController = ComposeUIViewController { 14 | App(viewModel = viewModel) 15 | } -------------------------------------------------------------------------------- /sample/chat-demo-mpp/common/src/iosMain/kotlin/io/github/jan/supabase/common/MPViewModel.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common 2 | 3 | import kotlinx.coroutines.CoroutineScope 4 | import kotlinx.coroutines.Dispatchers 5 | 6 | actual open class MPViewModel actual constructor() { 7 | actual val coroutineScope = CoroutineScope(Dispatchers.Default) 8 | } -------------------------------------------------------------------------------- /sample/chat-demo-mpp/common/src/iosMain/kotlin/io/github/jan/supabase/common/di/platformGoTrueConfig.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common.di 2 | 3 | import io.github.jan.supabase.auth.AuthConfig 4 | 5 | actual fun AuthConfig.platformGoTrueConfig() { 6 | scheme = "io.jan.supabase" 7 | host = "login" 8 | } -------------------------------------------------------------------------------- /sample/chat-demo-mpp/common/src/iosMain/kotlin/io/github/jan/supabase/common/di/viewModel.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common.di 2 | 3 | import org.koin.core.module.Module 4 | 5 | actual fun Module.viewModel() { 6 | single { createViewModule() } 7 | } -------------------------------------------------------------------------------- /sample/chat-demo-mpp/common/src/jsMain/kotlin/io/github/jan/supabase/common/MPViewModel.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common 2 | 3 | import kotlinx.coroutines.CoroutineScope 4 | import kotlinx.coroutines.Dispatchers 5 | 6 | actual open class MPViewModel actual constructor() { 7 | 8 | actual val coroutineScope = CoroutineScope(Dispatchers.Default) 9 | 10 | } -------------------------------------------------------------------------------- /sample/chat-demo-mpp/common/src/jsMain/kotlin/io/github/jan/supabase/common/di/platformGoTrueConfig.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common.di 2 | 3 | import io.github.jan.supabase.auth.AuthConfig 4 | 5 | actual fun AuthConfig.platformGoTrueConfig() = Unit -------------------------------------------------------------------------------- /sample/chat-demo-mpp/common/src/jsMain/kotlin/io/github/jan/supabase/common/di/viewModel.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common.di 2 | 3 | import org.koin.core.module.Module 4 | 5 | actual fun Module.viewModel() { 6 | single { createViewModule() } 7 | } -------------------------------------------------------------------------------- /sample/chat-demo-mpp/desktop/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.jetbrains.kotlin.gradle.dsl.JvmTarget 2 | 3 | plugins { 4 | id(libs.plugins.kotlin.multiplatform.get().pluginId) 5 | id(libs.plugins.compose.plugin.get().pluginId) 6 | alias(libs.plugins.compose.compiler) 7 | alias(libs.plugins.kotlinx.plugin.serialization) 8 | } 9 | 10 | group = "io.github.jan.supabase" 11 | version = "1.0-SNAPSHOT" 12 | 13 | 14 | kotlin { 15 | jvmToolchain(11) 16 | jvm { 17 | compilerOptions { 18 | jvmTarget = JvmTarget.JVM_11 19 | } 20 | } 21 | sourceSets { 22 | val jvmMain by getting { 23 | dependencies { 24 | implementation(project(":sample:chat-demo-mpp:common")) 25 | implementation(compose.desktop.currentOs) 26 | } 27 | } 28 | val jvmTest by getting 29 | } 30 | } 31 | 32 | compose.desktop.configureComposeDesktop("chat-demo-mpp") -------------------------------------------------------------------------------- /sample/chat-demo-mpp/desktop/src/jvmMain/kotlin/Main.kt: -------------------------------------------------------------------------------- 1 | import androidx.compose.ui.window.Window 2 | import androidx.compose.ui.window.application 3 | import io.github.jan.supabase.common.App 4 | import io.github.jan.supabase.common.ChatViewModel 5 | import io.github.jan.supabase.common.di.initKoin 6 | import org.koin.core.component.KoinComponent 7 | import org.koin.core.component.inject 8 | 9 | class RootComponent : KoinComponent { 10 | 11 | val viewModel: ChatViewModel by inject() 12 | 13 | } 14 | 15 | fun main() { 16 | initKoin() 17 | val root = RootComponent() 18 | application { 19 | Window(onCloseRequest = ::exitApplication, title = "Chat App") { 20 | App(root.viewModel) 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /sample/chat-demo-mpp/ios/chatdemoios.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /sample/chat-demo-mpp/ios/chatdemoios.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /sample/chat-demo-mpp/ios/chatdemoios.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /sample/chat-demo-mpp/ios/chatdemoios.xcodeproj/project.xcworkspace/xcuserdata/hieuvu.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supabase-community/supabase-kt/8d9207594a4f651f3f8ff2e4a5082787d7f15deb/sample/chat-demo-mpp/ios/chatdemoios.xcodeproj/project.xcworkspace/xcuserdata/hieuvu.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /sample/chat-demo-mpp/ios/chatdemoios.xcodeproj/project.xcworkspace/xcuserdata/hieuvu.xcuserdatad/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BuildLocationStyle 6 | UseAppPreferences 7 | CustomBuildLocationType 8 | RelativeToDerivedData 9 | DerivedDataLocationStyle 10 | Default 11 | ShowSharedSchemesAutomaticallyEnabled 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /sample/chat-demo-mpp/ios/chatdemoios.xcodeproj/xcuserdata/hieuvu.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | chatdemoios.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 2 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 6581F14D2B36C0CF00BA34A9 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /sample/chat-demo-mpp/ios/chatdemoios.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /sample/chat-demo-mpp/ios/chatdemoios.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /sample/chat-demo-mpp/ios/chatdemoios.xcworkspace/xcuserdata/hieuvu.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supabase-community/supabase-kt/8d9207594a4f651f3f8ff2e4a5082787d7f15deb/sample/chat-demo-mpp/ios/chatdemoios.xcworkspace/xcuserdata/hieuvu.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /sample/chat-demo-mpp/ios/chatdemoios/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /sample/chat-demo-mpp/ios/chatdemoios/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /sample/chat-demo-mpp/ios/chatdemoios/ContentView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentView.swift 3 | // chatdemoios 4 | // 5 | // Created by Hieu Vu on 23/12/2023. 6 | // 7 | 8 | import SwiftUI 9 | import common 10 | 11 | 12 | struct ContentView: UIViewControllerRepresentable { 13 | 14 | let viewModel: ChatViewModel = RootComponent().getViewModel() 15 | 16 | func makeUIViewController(context: Context) -> UIViewController { 17 | return AppKt.AppIos(viewModel: viewModel) 18 | } 19 | 20 | func updateUIViewController(_ uiViewController: UIViewController, context: Context) { 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /sample/chat-demo-mpp/ios/chatdemoios/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /sample/chat-demo-mpp/ios/chatdemoios/chatdemoios.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.files.user-selected.read-only 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /sample/chat-demo-mpp/ios/chatdemoios/chatdemoiosApp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // chatdemoiosApp.swift 3 | // chatdemoios 4 | // 5 | // Created by Hieu Vu on 23/12/2023. 6 | // 7 | 8 | import SwiftUI 9 | import common 10 | 11 | @main 12 | struct chatdemoiosApp: App { 13 | 14 | init() { 15 | KoinKt.doInitKoin(additionalConfiguration: {_ in}) 16 | } 17 | 18 | var body: some Scene { 19 | WindowGroup { 20 | ContentView() 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /sample/chat-demo-mpp/web/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id(libs.plugins.kotlin.multiplatform.get().pluginId) 3 | id(libs.plugins.compose.plugin.get().pluginId) 4 | alias(libs.plugins.compose.compiler) 5 | alias(libs.plugins.kotlinx.plugin.serialization) 6 | } 7 | 8 | group = "io.github.jan.supabase" 9 | version = "1.0-SNAPSHOT" 10 | 11 | 12 | kotlin { 13 | js(IR) { 14 | browser() 15 | binaries.executable() 16 | } 17 | sourceSets { 18 | val jsMain by getting { 19 | dependencies { 20 | implementation(project(":sample:chat-demo-mpp:common")) 21 | } 22 | } 23 | } 24 | } 25 | 26 | -------------------------------------------------------------------------------- /sample/chat-demo-mpp/web/src/jsMain/kotlin/Main.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. 2 | import androidx.compose.ui.ExperimentalComposeUiApi 3 | import androidx.compose.ui.window.CanvasBasedWindow 4 | import io.github.jan.supabase.common.App 5 | import io.github.jan.supabase.common.ChatViewModel 6 | import io.github.jan.supabase.common.di.initKoin 7 | import org.jetbrains.skiko.wasm.onWasmReady 8 | import org.koin.core.component.KoinComponent 9 | import org.koin.core.component.inject 10 | 11 | class RootComponent : KoinComponent { 12 | 13 | val viewModel: ChatViewModel by inject() 14 | 15 | } 16 | 17 | @OptIn(ExperimentalComposeUiApi::class) 18 | fun main() { 19 | initKoin() 20 | val root = RootComponent() 21 | onWasmReady { 22 | CanvasBasedWindow(title = "Demo Chat App") { 23 | App(root.viewModel) 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /sample/chat-demo-mpp/web/src/jsMain/resources/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Chat App 6 | 7 | 8 | 9 | 10 |
11 | 12 |
13 | 14 | 15 | -------------------------------------------------------------------------------- /sample/chat-demo-mpp/web/src/jsMain/resources/styles.css: -------------------------------------------------------------------------------- 1 | #root { 2 | width: 100%; 3 | height: 100vh; 4 | } 5 | 6 | #root > .compose-web-column > div { 7 | position: relative; 8 | } 9 | 10 | canvas { 11 | padding: 0; 12 | margin: auto; 13 | display: block; 14 | width: 800px; 15 | } -------------------------------------------------------------------------------- /sample/file-upload/README.md: -------------------------------------------------------------------------------- 1 | # File Upload Demo 2 | 3 | This is a demo of a file upload application using Compose Multiplatform, Koin and supabase-kt. 4 | 5 | **Available platforms:** Android, Desktop 6 | 7 | **Modules used:** Storage 8 | 9 | https://user-images.githubusercontent.com/26686035/233403165-26d87217-a0c9-4abd-b677-c367a5f1e4b9.mp4 10 | 11 | # Configuration 12 | 13 | To run the app, you need to create a Supabase project and create a public bucket with the permissions for anonymous users to upload files. 14 | 15 | Then you need to specify your bucket id, Supabase url and key in [supabaseModule.kt](https://github.com/supabase-community/supabase-kt/blob/master/sample/file-upload/common/src/commonMain/kotlin/io/github/jan/supabase/common/di/supabaseModule.kt) 16 | 17 | # Running 18 | 19 | To run the app, you need to run the following command in the root directory of the project: 20 | 21 | ./gradlew :sample:file-upload:desktop:runDistributable (Desktop) 22 | 23 | For android, use the IDE to run the app. 24 | -------------------------------------------------------------------------------- /sample/file-upload/android/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id(libs.plugins.compose.plugin.get().pluginId) 3 | alias(libs.plugins.compose.compiler) 4 | id("com.android.application") 5 | id(libs.plugins.kotlin.android.get().pluginId) 6 | alias(libs.plugins.kotlinx.plugin.serialization) 7 | } 8 | 9 | group = "io.github.jan.supabase" 10 | version = "1.0-SNAPSHOT" 11 | 12 | dependencies { 13 | implementation(project(":sample:file-upload:common")) 14 | implementation(libs.androidx.activity.compose) 15 | implementation(libs.accompanist.permissions) 16 | } 17 | 18 | android { 19 | configureApplicationAndroidTarget(JavaVersion.VERSION_11) 20 | kotlinOptions { 21 | jvmTarget = "11" 22 | } 23 | } -------------------------------------------------------------------------------- /sample/file-upload/android/src/main/java/io/github/jan/supabase/android/MainApplication.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.android 2 | 3 | import android.app.Application 4 | import io.github.jan.supabase.common.di.initKoin 5 | 6 | class MainApplication: Application() { 7 | 8 | override fun onCreate() { 9 | super.onCreate() 10 | initKoin() 11 | } 12 | 13 | } -------------------------------------------------------------------------------- /sample/file-upload/common/src/androidMain/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /sample/file-upload/common/src/androidMain/kotlin/io/github/jan/supabase/common/MPViewModel.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common 2 | 3 | import androidx.lifecycle.ViewModel 4 | import androidx.lifecycle.viewModelScope 5 | 6 | actual open class MPViewModel actual constructor(): ViewModel() { 7 | 8 | actual val coroutineScope = viewModelScope 9 | 10 | } -------------------------------------------------------------------------------- /sample/file-upload/common/src/androidMain/kotlin/io/github/jan/supabase/common/SupabaseInitializer.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common 2 | 3 | import android.content.Context 4 | import androidx.startup.Initializer 5 | 6 | private var appContext: Context? = null 7 | 8 | internal class SupabaseInitializer : Initializer { 9 | override fun create(context: Context): Context = context.applicationContext.also { appContext = it } 10 | 11 | override fun dependencies(): List>> = emptyList() 12 | 13 | } 14 | 15 | internal fun applicationContext(): Context = appContext ?: error("Application context not initialized") -------------------------------------------------------------------------------- /sample/file-upload/common/src/androidMain/kotlin/io/github/jan/supabase/common/di/viewModel.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common.di 2 | 3 | import org.koin.androidx.viewmodel.dsl.viewModel 4 | import org.koin.core.module.Module 5 | 6 | actual fun Module.viewModel() { 7 | viewModel { createViewModule() } 8 | } -------------------------------------------------------------------------------- /sample/file-upload/common/src/androidMain/kotlin/io/github/jan/supabase/common/parseFileTreeFromPath.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common 2 | 3 | import io.github.jan.supabase.storage.resumable.ResumableClient 4 | import io.github.jan.supabase.storage.resumable.ResumableUpload 5 | import io.github.vinceglb.filekit.core.PlatformFile 6 | import kotlinx.coroutines.Deferred 7 | 8 | actual fun parseFileTreeFromPath(path: String): List { 9 | TODO("Not yet implemented") 10 | } 11 | 12 | actual fun parseFileTreeFromURIs(paths: List): List { 13 | TODO("Not yet implemented") 14 | } 15 | 16 | actual suspend fun ResumableClient.continuePreviousPlatformUploads(): List> = throw UnsupportedOperationException() 17 | //not supported as you loose access to the uri's file after the app is closed -------------------------------------------------------------------------------- /sample/file-upload/common/src/androidMain/kotlin/io/github/jan/supabase/common/ui/components/AlertDialog.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common.ui.components 2 | 3 | import androidx.compose.material3.Button 4 | import androidx.compose.material3.Text 5 | import androidx.compose.runtime.Composable 6 | 7 | @Composable 8 | actual fun AlertDialog(text: String, close: () -> Unit) { 9 | androidx.compose.material3.AlertDialog( 10 | text = { Text(text) }, 11 | onDismissRequest = close, 12 | confirmButton = { Button(onClick = close) { Text("Ok") } } 13 | ) 14 | } -------------------------------------------------------------------------------- /sample/file-upload/common/src/androidMain/kotlin/io/github/jan/supabase/common/ui/utils/applyDragging.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common.ui.utils 2 | 3 | import androidx.compose.runtime.MutableState 4 | import androidx.compose.ui.Modifier 5 | 6 | actual fun Modifier.applyDragging(isDragging: MutableState, onSuccess: (List) -> Unit): Modifier = this -------------------------------------------------------------------------------- /sample/file-upload/common/src/androidMain/kotlin/io/github/jan/supabase/common/ui/utils/bytesToBitmap.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common.ui.utils 2 | 3 | import android.graphics.BitmapFactory 4 | import androidx.compose.ui.graphics.ImageBitmap 5 | import androidx.compose.ui.graphics.asImageBitmap 6 | 7 | actual fun bytesToBitmap(bytes: ByteArray): ImageBitmap = BitmapFactory.decodeByteArray (bytes, 0, bytes.size).asImageBitmap() -------------------------------------------------------------------------------- /sample/file-upload/common/src/commonMain/kotlin/io/github/jan/supabase/common/App.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common 2 | 3 | import androidx.compose.runtime.Composable 4 | import io.github.jan.supabase.common.ui.screen.UploadScreen 5 | 6 | @Composable 7 | fun App(viewModel: UploadViewModel) { 8 | UploadScreen(viewModel) 9 | } 10 | -------------------------------------------------------------------------------- /sample/file-upload/common/src/commonMain/kotlin/io/github/jan/supabase/common/Uploads.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common 2 | 3 | import io.github.jan.supabase.storage.resumable.Fingerprint 4 | import io.github.jan.supabase.storage.resumable.ResumableUploadState 5 | import io.github.vinceglb.filekit.core.PlatformFile 6 | import io.ktor.utils.io.ByteReadChannel 7 | 8 | sealed interface UploadState { 9 | 10 | val fingerprint: Fingerprint 11 | 12 | data class Loading(override val fingerprint: Fingerprint) : UploadState 13 | data class Loaded(override val fingerprint: Fingerprint, val state: ResumableUploadState) : UploadState 14 | 15 | } 16 | 17 | expect val PlatformFile.dataProducer: suspend (offset: Long) -> ByteReadChannel -------------------------------------------------------------------------------- /sample/file-upload/common/src/commonMain/kotlin/io/github/jan/supabase/common/Utils.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common 2 | 3 | import androidx.compose.ui.ExperimentalComposeUiApi 4 | import io.github.jan.supabase.storage.resumable.ResumableClient 5 | import io.github.jan.supabase.storage.resumable.ResumableUpload 6 | import io.github.vinceglb.filekit.core.PlatformFile 7 | import kotlinx.coroutines.Deferred 8 | 9 | @OptIn(ExperimentalComposeUiApi::class) 10 | expect fun parseFileTreeFromURIs(paths: List): List 11 | 12 | expect fun parseFileTreeFromPath(path: String): List 13 | 14 | expect suspend fun ResumableClient.continuePreviousPlatformUploads(): List> -------------------------------------------------------------------------------- /sample/file-upload/common/src/commonMain/kotlin/io/github/jan/supabase/common/di/koin.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common.di 2 | 3 | import org.koin.core.KoinApplication 4 | import org.koin.core.context.startKoin 5 | 6 | fun initKoin(additionalConfiguration: KoinApplication.() -> Unit = {}) { 7 | startKoin { 8 | modules(supabaseModule, viewModelModule) 9 | additionalConfiguration() 10 | } 11 | } -------------------------------------------------------------------------------- /sample/file-upload/common/src/commonMain/kotlin/io/github/jan/supabase/common/di/viewModelModule.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common.di 2 | 3 | import io.github.jan.supabase.common.UploadViewModel 4 | import org.koin.core.module.Module 5 | import org.koin.core.scope.Scope 6 | import org.koin.dsl.module 7 | 8 | expect fun Module.viewModel() 9 | 10 | fun Scope.createViewModule() = UploadViewModel(get()) 11 | 12 | val viewModelModule = module { 13 | viewModel() 14 | } -------------------------------------------------------------------------------- /sample/file-upload/common/src/commonMain/kotlin/io/github/jan/supabase/common/ui/components/AlertDialog.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common.ui.components 2 | 3 | import androidx.compose.runtime.Composable 4 | 5 | @Composable 6 | expect fun AlertDialog(text: String, close: () -> Unit) -------------------------------------------------------------------------------- /sample/file-upload/common/src/commonMain/kotlin/io/github/jan/supabase/common/ui/utils/Bitmap.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common.ui.utils 2 | 3 | import androidx.compose.ui.graphics.ImageBitmap 4 | 5 | expect fun bytesToBitmap(bytes: ByteArray): ImageBitmap -------------------------------------------------------------------------------- /sample/file-upload/common/src/commonMain/kotlin/io/github/jan/supabase/common/ui/utils/Dragging.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common.ui.utils 2 | 3 | import androidx.compose.runtime.MutableState 4 | import androidx.compose.ui.Modifier 5 | 6 | expect fun Modifier.applyDragging(isDragging: MutableState, onSuccess: (List) -> Unit): Modifier -------------------------------------------------------------------------------- /sample/file-upload/common/src/commonMain/kotlin/io/github/jan/supabase/common/ui/utils/FileSize.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common.ui.utils 2 | 3 | val Long.fileSize: String 4 | get() { 5 | val size = this.toDouble() 6 | return when { 7 | size < 1024 -> "${size.toLong()} B" 8 | size < 1024 * 1024 -> "${(size / 1024).toLong()} KB" 9 | size < 1024 * 1024 * 1024 -> "${(size / 1024 / 1024).toLong()} MB" 10 | else -> "${(size / 1024 / 1024 / 1024).toLong()} GB" 11 | } 12 | } -------------------------------------------------------------------------------- /sample/file-upload/common/src/desktopMain/kotlin/io/github/jan/supabase/common/MPViewModel.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common 2 | 3 | import kotlinx.coroutines.CoroutineScope 4 | import kotlinx.coroutines.Dispatchers 5 | 6 | actual open class MPViewModel actual constructor() { 7 | 8 | actual val coroutineScope = CoroutineScope(Dispatchers.Default) 9 | 10 | } -------------------------------------------------------------------------------- /sample/file-upload/common/src/desktopMain/kotlin/io/github/jan/supabase/common/Uploads.desktop.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common 2 | 3 | import io.github.vinceglb.filekit.core.PlatformFile 4 | import io.ktor.util.cio.readChannel 5 | import io.ktor.utils.io.ByteReadChannel 6 | 7 | actual val PlatformFile.dataProducer: suspend (offset: Long) -> ByteReadChannel get() = { offset -> 8 | this.file.readChannel(start = offset) 9 | } -------------------------------------------------------------------------------- /sample/file-upload/common/src/desktopMain/kotlin/io/github/jan/supabase/common/di/viewModel.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common.di 2 | 3 | import org.koin.core.module.Module 4 | 5 | actual fun Module.viewModel() { 6 | single { createViewModule() } 7 | } -------------------------------------------------------------------------------- /sample/file-upload/common/src/desktopMain/kotlin/io/github/jan/supabase/common/ui/utils/bytesToBitmap.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common.ui.utils 2 | 3 | import androidx.compose.ui.graphics.ImageBitmap 4 | import androidx.compose.ui.graphics.toComposeImageBitmap 5 | 6 | actual fun bytesToBitmap(bytes: ByteArray): ImageBitmap = org.jetbrains.skia.Image.makeFromEncoded(bytes).toComposeImageBitmap() -------------------------------------------------------------------------------- /sample/file-upload/desktop/src/jvmMain/kotlin/Main.kt: -------------------------------------------------------------------------------- 1 | import androidx.compose.ui.window.Window 2 | import androidx.compose.ui.window.application 3 | import io.github.jan.supabase.common.App 4 | import io.github.jan.supabase.common.UploadViewModel 5 | import io.github.jan.supabase.common.di.initKoin 6 | import org.koin.core.component.KoinComponent 7 | import org.koin.core.component.inject 8 | 9 | class RootComponent : KoinComponent { 10 | 11 | val viewModel: UploadViewModel by inject() 12 | 13 | } 14 | 15 | fun main() { 16 | initKoin() 17 | val root = RootComponent() 18 | application { 19 | Window(onCloseRequest = ::exitApplication, title = "File Upload") { 20 | App(root.viewModel) 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /sample/multi-factor-auth/android/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id(libs.plugins.compose.plugin.get().pluginId) 3 | alias(libs.plugins.compose.compiler) 4 | id("com.android.application") 5 | id(libs.plugins.kotlin.android.get().pluginId) 6 | alias(libs.plugins.kotlinx.plugin.serialization) 7 | } 8 | 9 | group = "io.github.jan.supabase" 10 | version = "1.0-SNAPSHOT" 11 | 12 | dependencies { 13 | implementation(libs.androidx.activity.compose) 14 | implementation(project(":sample:multi-factor-auth:common")) 15 | } 16 | 17 | android { 18 | configureApplicationAndroidTarget(JavaVersion.VERSION_11) 19 | kotlinOptions { 20 | jvmTarget = "11" 21 | } 22 | } -------------------------------------------------------------------------------- /sample/multi-factor-auth/android/src/main/java/io/github/jan/supabase/android/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.android 2 | 3 | import android.os.Bundle 4 | import androidx.activity.ComponentActivity 5 | import androidx.activity.compose.setContent 6 | import androidx.compose.material3.MaterialTheme 7 | import io.github.jan.supabase.auth.handleDeeplinks 8 | import io.github.jan.supabase.common.App 9 | import io.github.jan.supabase.common.AppViewModel 10 | import org.koin.android.ext.android.inject 11 | 12 | class MainActivity : ComponentActivity() { 13 | 14 | private val viewModel: AppViewModel by inject() 15 | 16 | override fun onCreate(savedInstanceState: Bundle?) { 17 | super.onCreate(savedInstanceState) 18 | viewModel.supabaseClient.handleDeeplinks(intent) 19 | setContent { 20 | MaterialTheme { 21 | App(viewModel) 22 | } 23 | } 24 | } 25 | 26 | } -------------------------------------------------------------------------------- /sample/multi-factor-auth/android/src/main/java/io/github/jan/supabase/android/MainApplication.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.android 2 | 3 | import android.app.Application 4 | import io.github.jan.supabase.common.di.initKoin 5 | 6 | class MainApplication: Application() { 7 | 8 | override fun onCreate() { 9 | super.onCreate() 10 | initKoin() 11 | } 12 | 13 | } -------------------------------------------------------------------------------- /sample/multi-factor-auth/common/src/androidMain/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /sample/multi-factor-auth/common/src/androidMain/kotlin/io/github/jan/supabase/common/MPViewModel.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common 2 | 3 | import androidx.lifecycle.ViewModel 4 | import androidx.lifecycle.viewModelScope 5 | 6 | actual open class MPViewModel actual constructor(): ViewModel() { 7 | 8 | actual val coroutineScope = viewModelScope 9 | 10 | } -------------------------------------------------------------------------------- /sample/multi-factor-auth/common/src/androidMain/kotlin/io/github/jan/supabase/common/di/platformGoTrueConfig.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common.di 2 | 3 | import io.github.jan.supabase.auth.AuthConfig 4 | 5 | actual fun AuthConfig.platformGoTrueConfig() { 6 | scheme = "io.jan.supabase" 7 | host = "login" 8 | } -------------------------------------------------------------------------------- /sample/multi-factor-auth/common/src/androidMain/kotlin/io/github/jan/supabase/common/di/viewModel.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common.di 2 | 3 | import org.koin.androidx.viewmodel.dsl.viewModel 4 | import org.koin.core.module.Module 5 | 6 | actual fun Module.viewModel() { 7 | viewModel { createViewModule() } 8 | } -------------------------------------------------------------------------------- /sample/multi-factor-auth/common/src/androidMain/kotlin/io/github/jan/supabase/common/ui/components/AlertDialog.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common.ui.components 2 | 3 | import androidx.compose.material3.Button 4 | import androidx.compose.material3.Text 5 | import androidx.compose.runtime.Composable 6 | 7 | @Composable 8 | actual fun AlertDialog(text: String, close: () -> Unit) { 9 | androidx.compose.material3.AlertDialog( 10 | text = { Text(text) }, 11 | onDismissRequest = close, 12 | confirmButton = { Button(onClick = close) { Text("Ok") } } 13 | ) 14 | } -------------------------------------------------------------------------------- /sample/multi-factor-auth/common/src/androidMain/kotlin/io/github/jan/supabase/common/ui/components/QRCode.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common.ui.components 2 | 3 | import androidx.compose.runtime.Composable 4 | import androidx.compose.runtime.remember 5 | import androidx.compose.ui.Modifier 6 | import androidx.compose.ui.platform.LocalContext 7 | import coil.ImageLoader 8 | import coil.compose.AsyncImage 9 | import coil.decode.SvgDecoder 10 | import java.nio.ByteBuffer 11 | 12 | @Composable 13 | actual fun QRCode(svgData: String, modifier: Modifier) { 14 | val context = LocalContext.current 15 | val imageLoader = remember { 16 | ImageLoader.Builder(context) 17 | .components { 18 | add(SvgDecoder.Factory()) 19 | } 20 | .build() 21 | } 22 | AsyncImage(model = ByteBuffer.wrap(svgData.toByteArray()), imageLoader = imageLoader, modifier = modifier, contentDescription = null) 23 | } -------------------------------------------------------------------------------- /sample/multi-factor-auth/common/src/commonMain/kotlin/io/github/jan/supabase/common/di/koin.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common.di 2 | 3 | import org.koin.core.KoinApplication 4 | import org.koin.core.context.startKoin 5 | 6 | fun initKoin(additionalConfiguration: KoinApplication.() -> Unit = {}) { 7 | startKoin { 8 | modules(supabaseModule, netModule, viewModelModule) 9 | additionalConfiguration() 10 | } 11 | } -------------------------------------------------------------------------------- /sample/multi-factor-auth/common/src/commonMain/kotlin/io/github/jan/supabase/common/di/netModule.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common.di 2 | 3 | import org.koin.dsl.module 4 | 5 | val netModule = module { 6 | 7 | } -------------------------------------------------------------------------------- /sample/multi-factor-auth/common/src/commonMain/kotlin/io/github/jan/supabase/common/di/supabaseModule.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common.di 2 | 3 | import io.github.jan.supabase.auth.Auth 4 | import io.github.jan.supabase.auth.AuthConfig 5 | import io.github.jan.supabase.createSupabaseClient 6 | import org.koin.dsl.module 7 | 8 | expect fun AuthConfig.platformGoTrueConfig() 9 | 10 | val supabaseModule = module { 11 | single { 12 | createSupabaseClient( 13 | supabaseUrl = "YOUR_URL", 14 | supabaseKey = "YOUR_KEY" 15 | ) { 16 | install(Auth) { 17 | platformGoTrueConfig() 18 | } 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /sample/multi-factor-auth/common/src/commonMain/kotlin/io/github/jan/supabase/common/di/viewModelModule.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common.di 2 | 3 | import io.github.jan.supabase.common.AppViewModel 4 | import org.koin.core.module.Module 5 | import org.koin.core.scope.Scope 6 | import org.koin.dsl.module 7 | 8 | expect fun Module.viewModel() 9 | 10 | fun Scope.createViewModule() = AppViewModel(get()) 11 | 12 | val viewModelModule = module { 13 | viewModel() 14 | } -------------------------------------------------------------------------------- /sample/multi-factor-auth/common/src/commonMain/kotlin/io/github/jan/supabase/common/ui/components/AlertDialog.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common.ui.components 2 | 3 | import androidx.compose.runtime.Composable 4 | 5 | @Composable 6 | expect fun AlertDialog(text: String, close: () -> Unit) -------------------------------------------------------------------------------- /sample/multi-factor-auth/common/src/commonMain/kotlin/io/github/jan/supabase/common/ui/components/QRCode.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common.ui.components 2 | 3 | import androidx.compose.runtime.Composable 4 | import androidx.compose.ui.Modifier 5 | 6 | @Composable 7 | expect fun QRCode( 8 | svgData: String, 9 | modifier: Modifier = Modifier, 10 | ) -------------------------------------------------------------------------------- /sample/multi-factor-auth/common/src/commonMain/kotlin/io/github/jan/supabase/common/ui/screen/MfaScreen.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common.ui.screen 2 | 3 | import androidx.compose.runtime.Composable 4 | import androidx.compose.runtime.collectAsState 5 | import androidx.compose.runtime.getValue 6 | import io.github.jan.supabase.auth.mfa.MfaStatus 7 | import io.github.jan.supabase.common.AppViewModel 8 | 9 | @Composable 10 | fun MfaScreen(viewModel: AppViewModel) { 11 | val status by viewModel.statusFlow.collectAsState(MfaStatus(false, false)) 12 | when { 13 | status.enabled && status.active -> { //only when logged in using mfa & mfa enabled 14 | AppScreen(viewModel) 15 | } 16 | status.enabled && !status.active -> { //show only when mfa enabled & not logged in using mfa 17 | MfaLoginScreen(viewModel) 18 | } 19 | else -> { //show only when logged in using mfa and mfa disabled or not set up 20 | MfaSetupScreen(viewModel) 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /sample/multi-factor-auth/common/src/desktopMain/kotlin/io/github/jan/supabase/common/MPViewModel.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common 2 | 3 | import kotlinx.coroutines.CoroutineScope 4 | import kotlinx.coroutines.Dispatchers 5 | 6 | actual open class MPViewModel actual constructor() { 7 | 8 | actual val coroutineScope = CoroutineScope(Dispatchers.Default) 9 | 10 | } -------------------------------------------------------------------------------- /sample/multi-factor-auth/common/src/desktopMain/kotlin/io/github/jan/supabase/common/di/platformGoTrueConfig.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common.di 2 | 3 | import io.github.jan.supabase.auth.AuthConfig 4 | 5 | actual fun AuthConfig.platformGoTrueConfig() { 6 | httpCallbackConfig { htmlTitle = "Chat App" } 7 | } -------------------------------------------------------------------------------- /sample/multi-factor-auth/common/src/desktopMain/kotlin/io/github/jan/supabase/common/di/viewModel.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common.di 2 | 3 | import org.koin.core.module.Module 4 | 5 | actual fun Module.viewModel() { 6 | single { createViewModule() } 7 | } -------------------------------------------------------------------------------- /sample/multi-factor-auth/common/src/desktopMain/kotlin/io/github/jan/supabase/common/ui/components/QRCode.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common.ui.components 2 | 3 | import androidx.compose.foundation.Image 4 | import androidx.compose.runtime.Composable 5 | import androidx.compose.runtime.remember 6 | import androidx.compose.ui.Modifier 7 | import androidx.compose.ui.res.loadSvgPainter 8 | import androidx.compose.ui.unit.Density 9 | 10 | @Composable 11 | actual fun QRCode(svgData: String, modifier: Modifier) { 12 | val painter = remember { loadSvgPainter(svgData.byteInputStream(), Density(1f)) } 13 | Image(painter, null, modifier) 14 | } -------------------------------------------------------------------------------- /sample/multi-factor-auth/common/src/jsMain/kotlin/io/github/jan/supabase/common/MPViewModel.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common 2 | 3 | import kotlinx.coroutines.CoroutineScope 4 | import kotlinx.coroutines.Dispatchers 5 | 6 | actual open class MPViewModel actual constructor() { 7 | 8 | actual val coroutineScope = CoroutineScope(Dispatchers.Default) 9 | 10 | } -------------------------------------------------------------------------------- /sample/multi-factor-auth/common/src/jsMain/kotlin/io/github/jan/supabase/common/di/platformGoTrueConfig.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common.di 2 | 3 | import io.github.jan.supabase.auth.AuthConfig 4 | 5 | actual fun AuthConfig.platformGoTrueConfig() { 6 | } -------------------------------------------------------------------------------- /sample/multi-factor-auth/common/src/jsMain/kotlin/io/github/jan/supabase/common/di/viewModel.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common.di 2 | 3 | import org.koin.core.module.Module 4 | 5 | actual fun Module.viewModel() { 6 | single { createViewModule() } 7 | } -------------------------------------------------------------------------------- /sample/multi-factor-auth/common/src/jsMain/kotlin/io/github/jan/supabase/common/ui/components/AlertDialog.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common.ui.components 2 | 3 | import androidx.compose.runtime.Composable 4 | import kotlinx.browser.window 5 | 6 | @Composable 7 | actual fun AlertDialog(text: String, close: () -> Unit) { 8 | window.alert(text) 9 | } -------------------------------------------------------------------------------- /sample/multi-factor-auth/common/src/jsMain/kotlin/io/github/jan/supabase/common/ui/components/QRCode.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.common.ui.components 2 | 3 | import androidx.compose.foundation.Canvas 4 | import androidx.compose.runtime.Composable 5 | import androidx.compose.runtime.remember 6 | import androidx.compose.ui.Modifier 7 | import androidx.compose.ui.graphics.drawscope.drawIntoCanvas 8 | import androidx.compose.ui.graphics.nativeCanvas 9 | import org.jetbrains.skia.Data 10 | import org.jetbrains.skia.svg.SVGDOM 11 | 12 | @Composable 13 | actual fun QRCode(svgData: String, modifier: Modifier) { 14 | val svgDom = remember { SVGDOM(data = Data.makeFromBytes(svgData.encodeToByteArray())) } 15 | Canvas(modifier = modifier) { 16 | svgDom.setContainerSize(size.width,size.height) 17 | drawIntoCanvas { canvas -> 18 | svgDom.render(canvas.nativeCanvas) 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /sample/multi-factor-auth/desktop/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.jetbrains.kotlin.gradle.dsl.JvmTarget 2 | 3 | plugins { 4 | id(libs.plugins.compose.plugin.get().pluginId) 5 | alias(libs.plugins.compose.compiler) 6 | id(libs.plugins.kotlin.multiplatform.get().pluginId) 7 | alias(libs.plugins.kotlinx.plugin.serialization) 8 | } 9 | 10 | group = "io.github.jan.supabase" 11 | version = "1.0-SNAPSHOT" 12 | 13 | 14 | kotlin { 15 | jvmToolchain(11) 16 | jvm { 17 | compilerOptions { 18 | jvmTarget = JvmTarget.JVM_11 19 | } 20 | } 21 | sourceSets { 22 | val jvmMain by getting { 23 | dependencies { 24 | implementation(project(":sample:multi-factor-auth:common")) 25 | implementation(compose.desktop.currentOs) 26 | } 27 | } 28 | val jvmTest by getting 29 | } 30 | } 31 | 32 | compose.desktop.configureComposeDesktop("multi-factor-auth") -------------------------------------------------------------------------------- /sample/multi-factor-auth/desktop/src/jvmMain/kotlin/Main.kt: -------------------------------------------------------------------------------- 1 | import androidx.compose.ui.window.Window 2 | import androidx.compose.ui.window.application 3 | import io.github.jan.supabase.common.App 4 | import io.github.jan.supabase.common.AppViewModel 5 | import io.github.jan.supabase.common.di.initKoin 6 | import org.koin.core.component.KoinComponent 7 | import org.koin.core.component.inject 8 | 9 | class RootComponent : KoinComponent { 10 | 11 | val viewModel: AppViewModel by inject() 12 | 13 | } 14 | 15 | fun main() { 16 | initKoin() 17 | val root = RootComponent() 18 | application { 19 | Window(onCloseRequest = ::exitApplication, title = "MFA App") { 20 | App(root.viewModel) 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /sample/multi-factor-auth/web/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id(libs.plugins.compose.plugin.get().pluginId) 3 | alias(libs.plugins.compose.compiler) 4 | id(libs.plugins.kotlin.multiplatform.get().pluginId) 5 | alias(libs.plugins.kotlinx.plugin.serialization) 6 | } 7 | 8 | group = "io.github.jan.supabase" 9 | version = "1.0-SNAPSHOT" 10 | 11 | 12 | kotlin { 13 | applyDefaultHierarchyTemplate() 14 | js(IR) { 15 | browser() 16 | binaries.executable() 17 | } 18 | sourceSets { 19 | val jsMain by getting { 20 | dependencies { 21 | implementation(project(":sample:multi-factor-auth:common")) 22 | } 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /sample/multi-factor-auth/web/src/jsMain/kotlin/Main.kt: -------------------------------------------------------------------------------- 1 | import androidx.compose.ui.ExperimentalComposeUiApi 2 | import androidx.compose.ui.window.CanvasBasedWindow 3 | import io.github.jan.supabase.common.App 4 | import io.github.jan.supabase.common.AppViewModel 5 | import io.github.jan.supabase.common.di.initKoin 6 | import org.jetbrains.skiko.wasm.onWasmReady 7 | import org.koin.core.component.KoinComponent 8 | import org.koin.core.component.inject 9 | 10 | class RootComponent : KoinComponent { 11 | 12 | val viewModel: AppViewModel by inject() 13 | 14 | } 15 | 16 | @OptIn(ExperimentalComposeUiApi::class) 17 | fun main() { 18 | initKoin() 19 | val root = RootComponent() 20 | onWasmReady { 21 | CanvasBasedWindow(title = "MFA App") { 22 | App(root.viewModel) 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /sample/multi-factor-auth/web/src/jsMain/resources/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Chat App 6 | 7 | 8 | 9 | 10 |
11 | 12 |
13 | 14 | 15 | -------------------------------------------------------------------------------- /sample/multi-factor-auth/web/src/jsMain/resources/styles.css: -------------------------------------------------------------------------------- 1 | #root { 2 | width: 100%; 3 | height: 100vh; 4 | } 5 | 6 | #root > .compose-web-column > div { 7 | position: relative; 8 | } 9 | 10 | canvas { 11 | padding: 0; 12 | margin: auto; 13 | display: block; 14 | width: 800px; 15 | } -------------------------------------------------------------------------------- /serializers/Jackson/README.md: -------------------------------------------------------------------------------- 1 | # Jackson Serializer 2 | 3 | # Installation 4 | 5 | ### Add the module to your project 6 | 7 | ```kotlin 8 | dependencies { 9 | implementation("io.github.jan-tennert.supabase:serializer-jackson:VERSION") 10 | } 11 | ``` -------------------------------------------------------------------------------- /serializers/Jackson/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id(libs.plugins.kotlin.multiplatform.get().pluginId) 3 | id(libs.plugins.android.library.get().pluginId) 4 | } 5 | 6 | description = "Extends supabase-kt with a Jackson Serializer" 7 | 8 | repositories { 9 | mavenCentral() 10 | } 11 | 12 | @OptIn(org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi::class) 13 | kotlin { 14 | defaultConfig() 15 | configuredAndroidTarget() 16 | configuredJvmTarget() 17 | sourceSets { 18 | val commonMain by getting { 19 | dependencies { 20 | addModules(SupabaseModule.SUPABASE) 21 | api(libs.bundles.jackson) 22 | } 23 | } 24 | val commonTest by getting { 25 | dependencies { 26 | implementation(libs.bundles.testing) 27 | implementation(project(":test-common")) 28 | } 29 | } 30 | } 31 | } 32 | 33 | configureLibraryAndroidTarget() -------------------------------------------------------------------------------- /serializers/Jackson/src/commonMain/kotlin/io/github/jan/supabase/serializer/JacksonSerializer.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.serializer 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper 4 | import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper 5 | import io.github.jan.supabase.SupabaseSerializer 6 | import kotlin.reflect.KType 7 | import kotlin.reflect.javaType 8 | 9 | /** 10 | * A [SupabaseSerializer] that uses jackson-module-kotlin 11 | */ 12 | class JacksonSerializer(private val mapper: ObjectMapper = jacksonObjectMapper()) : SupabaseSerializer { 13 | 14 | override fun encode(type: KType, value: T): String = mapper.writeValueAsString(value) 15 | 16 | @OptIn(ExperimentalStdlibApi::class) 17 | override fun decode(type: KType, value: String): T = mapper.readValue(value, mapper.constructType(type.javaType)) 18 | 19 | } -------------------------------------------------------------------------------- /serializers/Jackson/src/commonTest/kotlin/JacksonSerializerTest.kt: -------------------------------------------------------------------------------- 1 | import io.github.jan.supabase.encode 2 | import io.github.jan.supabase.serializer.JacksonSerializer 3 | import io.github.jan.supabase.testing.createMockedSupabaseClient 4 | import kotlin.reflect.typeOf 5 | import kotlin.test.Test 6 | import kotlin.test.assertEquals 7 | 8 | class JacksonSerializerTest { 9 | 10 | @Test 11 | fun testJacksonSerializer() { 12 | val serializer = JacksonSerializer() 13 | val supabaseClient = createMockedSupabaseClient( 14 | configuration = { 15 | defaultSerializer = serializer 16 | } 17 | ) 18 | assertEquals(serializer,supabaseClient.defaultSerializer) 19 | val value = mapOf("key" to "value") 20 | val encoded = serializer.encode(value) 21 | val decoded = serializer.decode>(typeOf>(), encoded) 22 | assertEquals(value, decoded) 23 | } 24 | 25 | } -------------------------------------------------------------------------------- /serializers/Moshi/README.md: -------------------------------------------------------------------------------- 1 | # Moshi Serializer 2 | 3 | # Installation 4 | 5 | ### Add the module to your project 6 | 7 | ```kotlin 8 | dependencies { 9 | implementation("io.github.jan-tennert.supabase:serializer-moshi:VERSION") 10 | } 11 | ``` -------------------------------------------------------------------------------- /serializers/Moshi/src/commonMain/kotlin/io/github/jan/supabase/serializer/MoshiSerializer.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.serializer 2 | 3 | import com.squareup.moshi.Moshi 4 | import com.squareup.moshi.adapter 5 | import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory 6 | import io.github.jan.supabase.SupabaseSerializer 7 | import kotlin.reflect.KType 8 | 9 | /** 10 | * A [SupabaseSerializer] that uses moshi 11 | */ 12 | class MoshiSerializer( 13 | private val moshi: Moshi = Moshi.Builder().addLast(KotlinJsonAdapterFactory()).build() 14 | ) : SupabaseSerializer { 15 | 16 | @OptIn(ExperimentalStdlibApi::class) 17 | override fun encode(type: KType, value: T): String = moshi.adapter(type).toJson(value) 18 | 19 | @OptIn(ExperimentalStdlibApi::class) 20 | override fun decode(type: KType, value: String): T { 21 | val adapter = moshi.adapter(type) 22 | return adapter.fromJson(value) ?: error("Failed to decode $value") 23 | } 24 | 25 | } -------------------------------------------------------------------------------- /serializers/Moshi/src/commonTest/kotlin/MoshiSerializerTest.kt: -------------------------------------------------------------------------------- 1 | import io.github.jan.supabase.encode 2 | import io.github.jan.supabase.serializer.MoshiSerializer 3 | import io.github.jan.supabase.testing.createMockedSupabaseClient 4 | import org.junit.Test 5 | import kotlin.reflect.typeOf 6 | import kotlin.test.assertEquals 7 | 8 | class MoshiSerializerTest { 9 | 10 | @Test 11 | fun testMoshiSerializer() { 12 | val serializer = MoshiSerializer() 13 | val supabaseClient = createMockedSupabaseClient( 14 | configuration = { 15 | defaultSerializer = serializer 16 | } 17 | ) 18 | assertEquals(serializer, supabaseClient.defaultSerializer) 19 | val value = mapOf("key" to "value") 20 | val encoded = serializer.encode(value) 21 | val decoded = serializer.decode>(typeOf>(), encoded) 22 | assertEquals(value, decoded) 23 | } 24 | 25 | } -------------------------------------------------------------------------------- /test-common/src/androidMain/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /test-common/src/commonMain/kotlin/io/github/jan/supabase/testing/RouteUtils.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.testing 2 | 3 | import io.ktor.http.Url 4 | 5 | fun Url.pathAfterVersion(): String { 6 | return encodedPath.substringAfter("/v1") 7 | } -------------------------------------------------------------------------------- /test-common/src/commonMain/kotlin/io/github/jan/supabase/testing/TestUtils.kt: -------------------------------------------------------------------------------- 1 | package io.github.jan.supabase.testing 2 | 3 | import io.ktor.http.HttpMethod 4 | import kotlin.test.assertEquals 5 | 6 | fun assertMethodIs(expected: HttpMethod, actual: HttpMethod) { 7 | assertEquals(expected, actual, "Method should be $expected") 8 | } 9 | 10 | fun assertPathIs(expected: String, actual: String) { 11 | assertEquals(expected, actual, "Path should be '$expected'") 12 | } -------------------------------------------------------------------------------- /test-common/src/commonTest/kotlin/DummySerializer.kt: -------------------------------------------------------------------------------- 1 | import io.github.jan.supabase.SupabaseSerializer 2 | import kotlin.reflect.KType 3 | 4 | class DummySerializer: SupabaseSerializer { 5 | override fun encode(type: KType, value: T): String { 6 | TODO("Not yet implemented") 7 | } 8 | 9 | override fun decode(type: KType, value: String): T { 10 | TODO("Not yet implemented") 11 | } 12 | } --------------------------------------------------------------------------------