diff --git a/API_DESIGN.md b/API_DESIGN.md index f82b02c..e667b2f 100644 --- a/API_DESIGN.md +++ b/API_DESIGN.md @@ -189,15 +189,30 @@ fixed; the rest are deliberate, recorded decisions. - Removed two exact duplicates: core `toResultFlow()` (≡ `asFlow()`) and realtime `statusFlow()` (≡ the `status` property). +**Perfection pass — every enum now one casing, every remaining outlier resolved:** +- **All enum entries are now `UPPER_SNAKE`, SDK-wide.** The two PascalCase hold-outs + (`SupabaseErrorCategory` = `CONFLICT`/`NOT_FOUND`/… and `TextSearchType` = `RAW`/`PLAIN`/`PHRASE`/ + `WEB_SEARCH`) were converted to match the ~38 other enums. Both were verified non-wire + (`SupabaseErrorCategory` is derived from HTTP status; `TextSearchType` carries its PostgREST token + in a constructor arg), so the change is name-only. +- `RealtimeSubscription.channel: String` → **`channelName`** — it returns a name, not a + `RealtimeChannel`; this also aligns it with the builder's existing `channelName`. +- `sync.Record` / `sync.Cursor` → **`SyncRecord`** / **`SyncCursor`** — qualified domain nouns + (matching `PendingChange`/`PullResult`), and `SyncCursor` no longer collides with the generated + `…store.db.Cursor`. +- storage `ObjectListV2Result` → **`ObjectListV2Response`** (the lone `*Result` among `*Response` + list DTOs); auth-admin `AuditLogEntry` → **`AuditLogEvent`** (the method is `listAuditLogEvents`); + auth-admin OIDC `userinfoUrl` → **`userInfoUrl`** (camelCase, siblings `authorizationUrl`/ + `tokenUrl`; `@SerialName` keeps the `userinfo_url` wire field); client config `logLevel` → + **`httpLogLevel`** (disambiguates the wire-verbosity `HttpLogLevel` from logger-severity + `SupabaseLogLevel`). + **Deliberate — kept by design (recorded so they aren't "fixed" later):** -- **Enum-entry casing is intentionally mixed.** Wire-mapped / option / SQL-keyword enums use - `UPPER_SNAKE` (`Order.ASC`, `ResponseFormat.GEOJSON`, every `@SerialName`-backed enum); the two - pure-domain category sets — `SupabaseErrorCategory` and `TextSearchType` — use `PascalCase` - because they read as a closed set of named conditions at a `when`/`onFailureCategory` call site. - Both styles are sanctioned by the Kotlin style guide; the split is by role, not by accident. - **Filter-operator vocabulary** (`eq`/`neq` abbreviated, `greater`/`greaterEq` spelled, range ops `rangeGt`/`rangeGte`) is kept — the query DSL was reviewed and blessed in the main audit; - the equality/ordering shorthands match what query-DSL users reach for. Not reopened. + the range ops are a distinct PostgREST range-operator family ("strictly right of", "doesn't + extend left") deliberately mirroring the supabase-js range tokens, so `rangeGreater` would + wrongly imply scalar-greater semantics. Not reopened. - **Factory verbs differ by what they return**, on purpose: `Supabase.create` (root builder), `createXClient` (feature clients), `googleAuthProvider`/`appleAuthProvider` (return a provider *descriptor* you hand to config, not a client), `openOfflineSyncStore` (opens a DB-backed @@ -210,10 +225,14 @@ fixed; the rest are deliberate, recorded decisions. - **`*OrThrow` / `*WithResult` / `*WithAck` suffixes** are the deliberate explicit-exception / await-confirmation escape hatches on top of the Result-first defaults — kept, not collapsed. -**Deferred (real but not worth pre-freeze churn; the sync surface isn't published in 1.0):** -- `sync-core` `Record`/`Cursor` → `SyncRecord`/`SyncCursor` (collide with `java.lang.Record`) — - ~285 references across the sync tree; the sync modules' publishing is deferred, so this can ride - the same train as their first publish. -- `RealtimeSubscription.channel: String` → `channelName` (a name-trap, but `.channel` is too - ambiguous to rename mechanically without risk) and storage `ObjectListV2Result`→`*Response` - (suffix polish, 69 references) — both additive-safe to revisit. +**Still deliberately kept (renaming would not be an improvement):** +- **Factory verbs differ by what they return**, on purpose: `Supabase.create` (root builder), + `createXClient` (feature clients), `googleAuthProvider`/`appleAuthProvider` (return a provider + *descriptor* you hand to config, not a client), `openOfflineSyncStore` (opens a DB-backed resource). +- **`rpcTyped`=single vs `selectTyped`=list** — an RPC returns one value by nature, a `select` + returns rows; `rpcListTyped`/`rpcSingleTyped` make the other cardinalities explicit. +- **Pseudo-namespaced auth methods** (`mfa*`/`oauth*`/`passkey*`) read noun-first by design — a + flattened stand-in for sub-clients; admin stays verb-first CRUD. `getUserById`/`updateUserById` + keep `ById` because the non-admin `getUser` already means "by access token." +- **`*OrThrow` / `*WithResult` / `*WithAck` suffixes** are the deliberate explicit-exception / + await-confirmation escape hatches on top of the Result-first defaults. diff --git a/CHANGELOG.md b/CHANGELOG.md index 84541b4..5d45c11 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -129,8 +129,26 @@ each call site. - Storage `VectorDistanceMetric.DOTPRODUCT` → **`DOT_PRODUCT`** — legible entry next to `COSINE` / `EUCLIDEAN`; the `dotproduct` wire value is unchanged (via `@SerialName`). - Auth-admin OIDC `jwksUri` → **`jwksUrl`** — its siblings are all `*Url` - (`authorizationUrl` / `tokenUrl` / `userinfoUrl`); the `jwks_uri` wire field is + (`authorizationUrl` / `tokenUrl` / `userInfoUrl`); the `jwks_uri` wire field is unchanged (via `@SerialName`). +- **Final naming pass, part 2 — every enum entry is now `UPPER_SNAKE` and every remaining + outlier is resolved:** + - **`SupabaseErrorCategory`** entries `Conflict`/`NotFound`/`Unauthorized`/`RateLimited`/ + `Validation`/`Internal`/`Network`/`Unknown` → **`CONFLICT`/`NOT_FOUND`/`UNAUTHORIZED`/ + `RATE_LIMITED`/`VALIDATION`/`INTERNAL`/`NETWORK`/`UNKNOWN`**, and **`TextSearchType`** + `Raw`/`Plain`/`Phrase`/`Websearch` → **`RAW`/`PLAIN`/`PHRASE`/`WEB_SEARCH`** — the last two + PascalCase enums now match the ~38 others. Both are name-only (the wire values live in HTTP + status mapping / a constructor arg, not the entry name). + - Realtime `RealtimeSubscription.channel` → **`channelName`** — it returns a name, not a + `RealtimeChannel` (and it now matches the builder's `channelName`). + - Sync `Record` / `Cursor` → **`SyncRecord`** / **`SyncCursor`** — qualified domain nouns; + `SyncCursor` no longer collides with the generated SQLDelight `Cursor`. + - Storage `ObjectListV2Result` → **`ObjectListV2Response`** (the lone `*Result` among the + `*Response` list DTOs); auth-admin `AuditLogEntry` → **`AuditLogEvent`** (the method is + `listAuditLogEvents`); auth-admin OIDC `userinfoUrl` → **`userInfoUrl`** (camelCase; + `userinfo_url` wire field unchanged). + - Client config `logLevel` → **`httpLogLevel`** — disambiguates wire-verbosity `HttpLogLevel` + from logger-severity `SupabaseLogLevel` at the call site. ### Removed (breaking) @@ -588,7 +606,7 @@ change is additive — no breaking changes. `captchaToken` and a defaulted `skip_http_redirect=true` on SSO so the URL call returns JSON instead of a 303 redirect; `webauthn` verification on `mfaVerify` (new `MfaWebauthnVerification` model). -- **Auth Admin** — `auditLogEvents(page, perPage)` returning `AuditLogEntry`; +- **Auth Admin** — `auditLogEvents(page, perPage)` returning `AuditLogEvent`; `updateFactor(userId, factorId, friendlyName)` returning the full `MfaFactor`. ### Fixed @@ -652,7 +670,7 @@ change is additive — no breaking changes. - **Auth** — `getSettings()` (`GET /auth/v1/settings`, reports which providers and flags the project has enabled) and `getHealth()` (`GET /auth/v1/health`); both decode tolerantly so unknown/missing keys don't break the response. -- **Postgrest** — `TextSearchType.Raw` exposes the bare `fts` operator +- **Postgrest** — `TextSearchType.RAW` exposes the bare `fts` operator (`to_tsquery`); a standalone `offset()`; `ReturnOption.HEADERS_ONLY` (`Prefer: return=headers-only`, an empty body that keeps headers like `Location`); and `replace()` — `PUT`-based single-row upsert by primary key. @@ -957,7 +975,7 @@ change is additive — no breaking changes. for issuing raw, non-JSON requests through the configured client (powers the resumable upload protocol). - **Errors:** network-aware error handling — `SupabaseError.httpStatus` and - `retryAfterSeconds`, a `SupabaseErrorCategory.Network` category, + `retryAfterSeconds`, a `SupabaseErrorCategory.NETWORK` category, `SupabaseErrorCategory.isRetryable`, `SupabaseError.isNetworkError()`, and an `onNetworkError { }` result handler. Transport now classifies timeouts and connection failures into `SupabaseErrorCodes.Client` codes so categorization diff --git a/README.md b/README.md index a26f1aa..4ed48ab 100644 --- a/README.md +++ b/README.md @@ -126,7 +126,7 @@ database.selectTyped(table = "todos") { } ``` -Errors carry a `category` (`Conflict`, `NotFound`, `Unauthorized`, `RateLimited`, `Validation`, `Internal`, `Network`, `Unknown`) so you can branch without parsing codes — plus chainable helpers like `onUnauthorized { }`, `onRateLimited { }` and `onNetworkError { }`. See [Results & Errors](https://androidpoet.github.io/supabase-kmp/results-and-errors) for the full surface. +Errors carry a `category` (`CONFLICT`, `NOT_FOUND`, `UNAUTHORIZED`, `RATE_LIMITED`, `VALIDATION`, `Internal`, `NETWORK`, `UNKNOWN`) so you can branch without parsing codes — plus chainable helpers like `onUnauthorized { }`, `onRateLimited { }` and `onNetworkError { }`. See [Results & Errors](https://androidpoet.github.io/supabase-kmp/results-and-errors) for the full surface. ## Modules diff --git a/supabase-auth-admin/api/android/supabase-auth-admin.api b/supabase-auth-admin/api/android/supabase-auth-admin.api index ce57494..4361fa6 100644 --- a/supabase-auth-admin/api/android/supabase-auth-admin.api +++ b/supabase-auth-admin/api/android/supabase-auth-admin.api @@ -106,16 +106,16 @@ public final class io/github/androidpoet/supabase/auth/admin/models/AdminUserAtt public final fun serializer ()Lkotlinx/serialization/KSerializer; } -public final class io/github/androidpoet/supabase/auth/admin/models/AuditLogEntry { - public static final field Companion Lio/github/androidpoet/supabase/auth/admin/models/AuditLogEntry$Companion; +public final class io/github/androidpoet/supabase/auth/admin/models/AuditLogEvent { + public static final field Companion Lio/github/androidpoet/supabase/auth/admin/models/AuditLogEvent$Companion; public fun (Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;Ljava/lang/String;Ljava/lang/String;)V public synthetic fun (Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun component1 ()Ljava/lang/String; public final fun component2 ()Lkotlinx/serialization/json/JsonObject; public final fun component3 ()Ljava/lang/String; public final fun component4 ()Ljava/lang/String; - public final fun copy (Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;Ljava/lang/String;Ljava/lang/String;)Lio/github/androidpoet/supabase/auth/admin/models/AuditLogEntry; - public static synthetic fun copy$default (Lio/github/androidpoet/supabase/auth/admin/models/AuditLogEntry;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lio/github/androidpoet/supabase/auth/admin/models/AuditLogEntry; + public final fun copy (Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;Ljava/lang/String;Ljava/lang/String;)Lio/github/androidpoet/supabase/auth/admin/models/AuditLogEvent; + public static synthetic fun copy$default (Lio/github/androidpoet/supabase/auth/admin/models/AuditLogEvent;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lio/github/androidpoet/supabase/auth/admin/models/AuditLogEvent; public fun equals (Ljava/lang/Object;)Z public final fun getCreatedAt ()Ljava/lang/String; public final fun getId ()Ljava/lang/String; @@ -125,18 +125,18 @@ public final class io/github/androidpoet/supabase/auth/admin/models/AuditLogEntr public fun toString ()Ljava/lang/String; } -public final synthetic class io/github/androidpoet/supabase/auth/admin/models/AuditLogEntry$$serializer : kotlinx/serialization/internal/GeneratedSerializer { - public static final field INSTANCE Lio/github/androidpoet/supabase/auth/admin/models/AuditLogEntry$$serializer; +public final synthetic class io/github/androidpoet/supabase/auth/admin/models/AuditLogEvent$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lio/github/androidpoet/supabase/auth/admin/models/AuditLogEvent$$serializer; public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; - public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lio/github/androidpoet/supabase/auth/admin/models/AuditLogEntry; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lio/github/androidpoet/supabase/auth/admin/models/AuditLogEvent; public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; - public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lio/github/androidpoet/supabase/auth/admin/models/AuditLogEntry;)V + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lio/github/androidpoet/supabase/auth/admin/models/AuditLogEvent;)V public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; } -public final class io/github/androidpoet/supabase/auth/admin/models/AuditLogEntry$Companion { +public final class io/github/androidpoet/supabase/auth/admin/models/AuditLogEvent$Companion { public final fun serializer ()Lkotlinx/serialization/KSerializer; } @@ -190,7 +190,7 @@ public final class io/github/androidpoet/supabase/auth/admin/models/CustomProvid public final fun getSkipNonceCheck ()Ljava/lang/Boolean; public final fun getTokenUrl ()Ljava/lang/String; public final fun getUpdatedAt ()Ljava/lang/String; - public final fun getUserinfoUrl ()Ljava/lang/String; + public final fun getUserInfoUrl ()Ljava/lang/String; public fun hashCode ()I public fun toString ()Ljava/lang/String; } @@ -254,7 +254,7 @@ public final class io/github/androidpoet/supabase/auth/admin/models/CustomProvid public final fun getScopes ()Ljava/util/List; public final fun getSkipNonceCheck ()Ljava/lang/Boolean; public final fun getTokenUrl ()Ljava/lang/String; - public final fun getUserinfoUrl ()Ljava/lang/String; + public final fun getUserInfoUrl ()Ljava/lang/String; public fun hashCode ()I public fun toString ()Ljava/lang/String; } @@ -359,7 +359,7 @@ public final class io/github/androidpoet/supabase/auth/admin/models/CustomProvid public final fun getScopes ()Ljava/util/List; public final fun getSkipNonceCheck ()Ljava/lang/Boolean; public final fun getTokenUrl ()Ljava/lang/String; - public final fun getUserinfoUrl ()Ljava/lang/String; + public final fun getUserInfoUrl ()Ljava/lang/String; public fun hashCode ()I public fun toString ()Ljava/lang/String; } diff --git a/supabase-auth-admin/api/jvm/supabase-auth-admin.api b/supabase-auth-admin/api/jvm/supabase-auth-admin.api index ce57494..4361fa6 100644 --- a/supabase-auth-admin/api/jvm/supabase-auth-admin.api +++ b/supabase-auth-admin/api/jvm/supabase-auth-admin.api @@ -106,16 +106,16 @@ public final class io/github/androidpoet/supabase/auth/admin/models/AdminUserAtt public final fun serializer ()Lkotlinx/serialization/KSerializer; } -public final class io/github/androidpoet/supabase/auth/admin/models/AuditLogEntry { - public static final field Companion Lio/github/androidpoet/supabase/auth/admin/models/AuditLogEntry$Companion; +public final class io/github/androidpoet/supabase/auth/admin/models/AuditLogEvent { + public static final field Companion Lio/github/androidpoet/supabase/auth/admin/models/AuditLogEvent$Companion; public fun (Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;Ljava/lang/String;Ljava/lang/String;)V public synthetic fun (Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun component1 ()Ljava/lang/String; public final fun component2 ()Lkotlinx/serialization/json/JsonObject; public final fun component3 ()Ljava/lang/String; public final fun component4 ()Ljava/lang/String; - public final fun copy (Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;Ljava/lang/String;Ljava/lang/String;)Lio/github/androidpoet/supabase/auth/admin/models/AuditLogEntry; - public static synthetic fun copy$default (Lio/github/androidpoet/supabase/auth/admin/models/AuditLogEntry;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lio/github/androidpoet/supabase/auth/admin/models/AuditLogEntry; + public final fun copy (Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;Ljava/lang/String;Ljava/lang/String;)Lio/github/androidpoet/supabase/auth/admin/models/AuditLogEvent; + public static synthetic fun copy$default (Lio/github/androidpoet/supabase/auth/admin/models/AuditLogEvent;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lio/github/androidpoet/supabase/auth/admin/models/AuditLogEvent; public fun equals (Ljava/lang/Object;)Z public final fun getCreatedAt ()Ljava/lang/String; public final fun getId ()Ljava/lang/String; @@ -125,18 +125,18 @@ public final class io/github/androidpoet/supabase/auth/admin/models/AuditLogEntr public fun toString ()Ljava/lang/String; } -public final synthetic class io/github/androidpoet/supabase/auth/admin/models/AuditLogEntry$$serializer : kotlinx/serialization/internal/GeneratedSerializer { - public static final field INSTANCE Lio/github/androidpoet/supabase/auth/admin/models/AuditLogEntry$$serializer; +public final synthetic class io/github/androidpoet/supabase/auth/admin/models/AuditLogEvent$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lio/github/androidpoet/supabase/auth/admin/models/AuditLogEvent$$serializer; public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; - public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lio/github/androidpoet/supabase/auth/admin/models/AuditLogEntry; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lio/github/androidpoet/supabase/auth/admin/models/AuditLogEvent; public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; - public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lio/github/androidpoet/supabase/auth/admin/models/AuditLogEntry;)V + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lio/github/androidpoet/supabase/auth/admin/models/AuditLogEvent;)V public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; } -public final class io/github/androidpoet/supabase/auth/admin/models/AuditLogEntry$Companion { +public final class io/github/androidpoet/supabase/auth/admin/models/AuditLogEvent$Companion { public final fun serializer ()Lkotlinx/serialization/KSerializer; } @@ -190,7 +190,7 @@ public final class io/github/androidpoet/supabase/auth/admin/models/CustomProvid public final fun getSkipNonceCheck ()Ljava/lang/Boolean; public final fun getTokenUrl ()Ljava/lang/String; public final fun getUpdatedAt ()Ljava/lang/String; - public final fun getUserinfoUrl ()Ljava/lang/String; + public final fun getUserInfoUrl ()Ljava/lang/String; public fun hashCode ()I public fun toString ()Ljava/lang/String; } @@ -254,7 +254,7 @@ public final class io/github/androidpoet/supabase/auth/admin/models/CustomProvid public final fun getScopes ()Ljava/util/List; public final fun getSkipNonceCheck ()Ljava/lang/Boolean; public final fun getTokenUrl ()Ljava/lang/String; - public final fun getUserinfoUrl ()Ljava/lang/String; + public final fun getUserInfoUrl ()Ljava/lang/String; public fun hashCode ()I public fun toString ()Ljava/lang/String; } @@ -359,7 +359,7 @@ public final class io/github/androidpoet/supabase/auth/admin/models/CustomProvid public final fun getScopes ()Ljava/util/List; public final fun getSkipNonceCheck ()Ljava/lang/Boolean; public final fun getTokenUrl ()Ljava/lang/String; - public final fun getUserinfoUrl ()Ljava/lang/String; + public final fun getUserInfoUrl ()Ljava/lang/String; public fun hashCode ()I public fun toString ()Ljava/lang/String; } diff --git a/supabase-auth-admin/api/supabase-auth-admin.klib.api b/supabase-auth-admin/api/supabase-auth-admin.klib.api index d1650b5..e8466bf 100644 --- a/supabase-auth-admin/api/supabase-auth-admin.klib.api +++ b/supabase-auth-admin/api/supabase-auth-admin.klib.api @@ -120,7 +120,7 @@ abstract interface io.github.androidpoet.supabase.auth.admin/AuthAdminClient { / abstract suspend fun getSsoProvider(kotlin/String): io.github.androidpoet.supabase.core.result/SupabaseResult // io.github.androidpoet.supabase.auth.admin/AuthAdminClient.getSsoProvider|getSsoProvider(kotlin.String){}[0] abstract suspend fun getUserById(kotlin/String): io.github.androidpoet.supabase.core.result/SupabaseResult // io.github.androidpoet.supabase.auth.admin/AuthAdminClient.getUserById|getUserById(kotlin.String){}[0] abstract suspend fun inviteUserByEmail(kotlin/String, kotlinx.serialization.json/JsonObject? = ..., kotlin/String? = ...): io.github.androidpoet.supabase.core.result/SupabaseResult // io.github.androidpoet.supabase.auth.admin/AuthAdminClient.inviteUserByEmail|inviteUserByEmail(kotlin.String;kotlinx.serialization.json.JsonObject?;kotlin.String?){}[0] - abstract suspend fun listAuditLogEvents(kotlin/Int? = ..., kotlin/Int? = ...): io.github.androidpoet.supabase.core.result/SupabaseResult> // io.github.androidpoet.supabase.auth.admin/AuthAdminClient.listAuditLogEvents|listAuditLogEvents(kotlin.Int?;kotlin.Int?){}[0] + abstract suspend fun listAuditLogEvents(kotlin/Int? = ..., kotlin/Int? = ...): io.github.androidpoet.supabase.core.result/SupabaseResult> // io.github.androidpoet.supabase.auth.admin/AuthAdminClient.listAuditLogEvents|listAuditLogEvents(kotlin.Int?;kotlin.Int?){}[0] abstract suspend fun listCustomProviders(io.github.androidpoet.supabase.auth.admin.models/CustomProviderType? = ...): io.github.androidpoet.supabase.core.result/SupabaseResult // io.github.androidpoet.supabase.auth.admin/AuthAdminClient.listCustomProviders|listCustomProviders(io.github.androidpoet.supabase.auth.admin.models.CustomProviderType?){}[0] abstract suspend fun listFactors(kotlin/String): io.github.androidpoet.supabase.core.result/SupabaseResult // io.github.androidpoet.supabase.auth.admin/AuthAdminClient.listFactors|listFactors(kotlin.String){}[0] abstract suspend fun listOAuthClients(kotlin/Int? = ..., kotlin/Int? = ...): io.github.androidpoet.supabase.core.result/SupabaseResult // io.github.androidpoet.supabase.auth.admin/AuthAdminClient.listOAuthClients|listOAuthClients(kotlin.Int?;kotlin.Int?){}[0] @@ -186,38 +186,38 @@ final class io.github.androidpoet.supabase.auth.admin.models/AdminUserAttributes } } -final class io.github.androidpoet.supabase.auth.admin.models/AuditLogEntry { // io.github.androidpoet.supabase.auth.admin.models/AuditLogEntry|null[0] - constructor (kotlin/String, kotlinx.serialization.json/JsonObject? = ..., kotlin/String? = ..., kotlin/String? = ...) // io.github.androidpoet.supabase.auth.admin.models/AuditLogEntry.|(kotlin.String;kotlinx.serialization.json.JsonObject?;kotlin.String?;kotlin.String?){}[0] - - final val createdAt // io.github.androidpoet.supabase.auth.admin.models/AuditLogEntry.createdAt|{}createdAt[0] - final fun (): kotlin/String? // io.github.androidpoet.supabase.auth.admin.models/AuditLogEntry.createdAt.|(){}[0] - final val id // io.github.androidpoet.supabase.auth.admin.models/AuditLogEntry.id|{}id[0] - final fun (): kotlin/String // io.github.androidpoet.supabase.auth.admin.models/AuditLogEntry.id.|(){}[0] - final val ipAddress // io.github.androidpoet.supabase.auth.admin.models/AuditLogEntry.ipAddress|{}ipAddress[0] - final fun (): kotlin/String? // io.github.androidpoet.supabase.auth.admin.models/AuditLogEntry.ipAddress.|(){}[0] - final val payload // io.github.androidpoet.supabase.auth.admin.models/AuditLogEntry.payload|{}payload[0] - final fun (): kotlinx.serialization.json/JsonObject? // io.github.androidpoet.supabase.auth.admin.models/AuditLogEntry.payload.|(){}[0] - - final fun component1(): kotlin/String // io.github.androidpoet.supabase.auth.admin.models/AuditLogEntry.component1|component1(){}[0] - final fun component2(): kotlinx.serialization.json/JsonObject? // io.github.androidpoet.supabase.auth.admin.models/AuditLogEntry.component2|component2(){}[0] - final fun component3(): kotlin/String? // io.github.androidpoet.supabase.auth.admin.models/AuditLogEntry.component3|component3(){}[0] - final fun component4(): kotlin/String? // io.github.androidpoet.supabase.auth.admin.models/AuditLogEntry.component4|component4(){}[0] - final fun copy(kotlin/String = ..., kotlinx.serialization.json/JsonObject? = ..., kotlin/String? = ..., kotlin/String? = ...): io.github.androidpoet.supabase.auth.admin.models/AuditLogEntry // io.github.androidpoet.supabase.auth.admin.models/AuditLogEntry.copy|copy(kotlin.String;kotlinx.serialization.json.JsonObject?;kotlin.String?;kotlin.String?){}[0] - final fun equals(kotlin/Any?): kotlin/Boolean // io.github.androidpoet.supabase.auth.admin.models/AuditLogEntry.equals|equals(kotlin.Any?){}[0] - final fun hashCode(): kotlin/Int // io.github.androidpoet.supabase.auth.admin.models/AuditLogEntry.hashCode|hashCode(){}[0] - final fun toString(): kotlin/String // io.github.androidpoet.supabase.auth.admin.models/AuditLogEntry.toString|toString(){}[0] - - final object $serializer : kotlinx.serialization.internal/GeneratedSerializer { // io.github.androidpoet.supabase.auth.admin.models/AuditLogEntry.$serializer|null[0] - final val descriptor // io.github.androidpoet.supabase.auth.admin.models/AuditLogEntry.$serializer.descriptor|{}descriptor[0] - final fun (): kotlinx.serialization.descriptors/SerialDescriptor // io.github.androidpoet.supabase.auth.admin.models/AuditLogEntry.$serializer.descriptor.|(){}[0] - - final fun childSerializers(): kotlin/Array> // io.github.androidpoet.supabase.auth.admin.models/AuditLogEntry.$serializer.childSerializers|childSerializers(){}[0] - final fun deserialize(kotlinx.serialization.encoding/Decoder): io.github.androidpoet.supabase.auth.admin.models/AuditLogEntry // io.github.androidpoet.supabase.auth.admin.models/AuditLogEntry.$serializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0] - final fun serialize(kotlinx.serialization.encoding/Encoder, io.github.androidpoet.supabase.auth.admin.models/AuditLogEntry) // io.github.androidpoet.supabase.auth.admin.models/AuditLogEntry.$serializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;io.github.androidpoet.supabase.auth.admin.models.AuditLogEntry){}[0] +final class io.github.androidpoet.supabase.auth.admin.models/AuditLogEvent { // io.github.androidpoet.supabase.auth.admin.models/AuditLogEvent|null[0] + constructor (kotlin/String, kotlinx.serialization.json/JsonObject? = ..., kotlin/String? = ..., kotlin/String? = ...) // io.github.androidpoet.supabase.auth.admin.models/AuditLogEvent.|(kotlin.String;kotlinx.serialization.json.JsonObject?;kotlin.String?;kotlin.String?){}[0] + + final val createdAt // io.github.androidpoet.supabase.auth.admin.models/AuditLogEvent.createdAt|{}createdAt[0] + final fun (): kotlin/String? // io.github.androidpoet.supabase.auth.admin.models/AuditLogEvent.createdAt.|(){}[0] + final val id // io.github.androidpoet.supabase.auth.admin.models/AuditLogEvent.id|{}id[0] + final fun (): kotlin/String // io.github.androidpoet.supabase.auth.admin.models/AuditLogEvent.id.|(){}[0] + final val ipAddress // io.github.androidpoet.supabase.auth.admin.models/AuditLogEvent.ipAddress|{}ipAddress[0] + final fun (): kotlin/String? // io.github.androidpoet.supabase.auth.admin.models/AuditLogEvent.ipAddress.|(){}[0] + final val payload // io.github.androidpoet.supabase.auth.admin.models/AuditLogEvent.payload|{}payload[0] + final fun (): kotlinx.serialization.json/JsonObject? // io.github.androidpoet.supabase.auth.admin.models/AuditLogEvent.payload.|(){}[0] + + final fun component1(): kotlin/String // io.github.androidpoet.supabase.auth.admin.models/AuditLogEvent.component1|component1(){}[0] + final fun component2(): kotlinx.serialization.json/JsonObject? // io.github.androidpoet.supabase.auth.admin.models/AuditLogEvent.component2|component2(){}[0] + final fun component3(): kotlin/String? // io.github.androidpoet.supabase.auth.admin.models/AuditLogEvent.component3|component3(){}[0] + final fun component4(): kotlin/String? // io.github.androidpoet.supabase.auth.admin.models/AuditLogEvent.component4|component4(){}[0] + final fun copy(kotlin/String = ..., kotlinx.serialization.json/JsonObject? = ..., kotlin/String? = ..., kotlin/String? = ...): io.github.androidpoet.supabase.auth.admin.models/AuditLogEvent // io.github.androidpoet.supabase.auth.admin.models/AuditLogEvent.copy|copy(kotlin.String;kotlinx.serialization.json.JsonObject?;kotlin.String?;kotlin.String?){}[0] + final fun equals(kotlin/Any?): kotlin/Boolean // io.github.androidpoet.supabase.auth.admin.models/AuditLogEvent.equals|equals(kotlin.Any?){}[0] + final fun hashCode(): kotlin/Int // io.github.androidpoet.supabase.auth.admin.models/AuditLogEvent.hashCode|hashCode(){}[0] + final fun toString(): kotlin/String // io.github.androidpoet.supabase.auth.admin.models/AuditLogEvent.toString|toString(){}[0] + + final object $serializer : kotlinx.serialization.internal/GeneratedSerializer { // io.github.androidpoet.supabase.auth.admin.models/AuditLogEvent.$serializer|null[0] + final val descriptor // io.github.androidpoet.supabase.auth.admin.models/AuditLogEvent.$serializer.descriptor|{}descriptor[0] + final fun (): kotlinx.serialization.descriptors/SerialDescriptor // io.github.androidpoet.supabase.auth.admin.models/AuditLogEvent.$serializer.descriptor.|(){}[0] + + final fun childSerializers(): kotlin/Array> // io.github.androidpoet.supabase.auth.admin.models/AuditLogEvent.$serializer.childSerializers|childSerializers(){}[0] + final fun deserialize(kotlinx.serialization.encoding/Decoder): io.github.androidpoet.supabase.auth.admin.models/AuditLogEvent // io.github.androidpoet.supabase.auth.admin.models/AuditLogEvent.$serializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0] + final fun serialize(kotlinx.serialization.encoding/Encoder, io.github.androidpoet.supabase.auth.admin.models/AuditLogEvent) // io.github.androidpoet.supabase.auth.admin.models/AuditLogEvent.$serializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;io.github.androidpoet.supabase.auth.admin.models.AuditLogEvent){}[0] } - final object Companion { // io.github.androidpoet.supabase.auth.admin.models/AuditLogEntry.Companion|null[0] - final fun serializer(): kotlinx.serialization/KSerializer // io.github.androidpoet.supabase.auth.admin.models/AuditLogEntry.Companion.serializer|serializer(){}[0] + final object Companion { // io.github.androidpoet.supabase.auth.admin.models/AuditLogEvent.Companion|null[0] + final fun serializer(): kotlinx.serialization/KSerializer // io.github.androidpoet.supabase.auth.admin.models/AuditLogEvent.Companion.serializer|serializer(){}[0] } } @@ -266,8 +266,8 @@ final class io.github.androidpoet.supabase.auth.admin.models/CustomProvider { // final fun (): kotlin/String? // io.github.androidpoet.supabase.auth.admin.models/CustomProvider.tokenUrl.|(){}[0] final val updatedAt // io.github.androidpoet.supabase.auth.admin.models/CustomProvider.updatedAt|{}updatedAt[0] final fun (): kotlin/String? // io.github.androidpoet.supabase.auth.admin.models/CustomProvider.updatedAt.|(){}[0] - final val userinfoUrl // io.github.androidpoet.supabase.auth.admin.models/CustomProvider.userinfoUrl|{}userinfoUrl[0] - final fun (): kotlin/String? // io.github.androidpoet.supabase.auth.admin.models/CustomProvider.userinfoUrl.|(){}[0] + final val userInfoUrl // io.github.androidpoet.supabase.auth.admin.models/CustomProvider.userInfoUrl|{}userInfoUrl[0] + final fun (): kotlin/String? // io.github.androidpoet.supabase.auth.admin.models/CustomProvider.userInfoUrl.|(){}[0] final fun component1(): kotlin/String // io.github.androidpoet.supabase.auth.admin.models/CustomProvider.component1|component1(){}[0] final fun component10(): kotlin.collections/Map? // io.github.androidpoet.supabase.auth.admin.models/CustomProvider.component10|component10(){}[0] @@ -351,8 +351,8 @@ final class io.github.androidpoet.supabase.auth.admin.models/CustomProviderCreat final fun (): kotlin/Boolean? // io.github.androidpoet.supabase.auth.admin.models/CustomProviderCreateRequest.skipNonceCheck.|(){}[0] final val tokenUrl // io.github.androidpoet.supabase.auth.admin.models/CustomProviderCreateRequest.tokenUrl|{}tokenUrl[0] final fun (): kotlin/String? // io.github.androidpoet.supabase.auth.admin.models/CustomProviderCreateRequest.tokenUrl.|(){}[0] - final val userinfoUrl // io.github.androidpoet.supabase.auth.admin.models/CustomProviderCreateRequest.userinfoUrl|{}userinfoUrl[0] - final fun (): kotlin/String? // io.github.androidpoet.supabase.auth.admin.models/CustomProviderCreateRequest.userinfoUrl.|(){}[0] + final val userInfoUrl // io.github.androidpoet.supabase.auth.admin.models/CustomProviderCreateRequest.userInfoUrl|{}userInfoUrl[0] + final fun (): kotlin/String? // io.github.androidpoet.supabase.auth.admin.models/CustomProviderCreateRequest.userInfoUrl.|(){}[0] final fun component1(): io.github.androidpoet.supabase.auth.admin.models/CustomProviderType // io.github.androidpoet.supabase.auth.admin.models/CustomProviderCreateRequest.component1|component1(){}[0] final fun component10(): kotlin.collections/Map? // io.github.androidpoet.supabase.auth.admin.models/CustomProviderCreateRequest.component10|component10(){}[0] @@ -457,8 +457,8 @@ final class io.github.androidpoet.supabase.auth.admin.models/CustomProviderUpdat final fun (): kotlin/Boolean? // io.github.androidpoet.supabase.auth.admin.models/CustomProviderUpdateRequest.skipNonceCheck.|(){}[0] final val tokenUrl // io.github.androidpoet.supabase.auth.admin.models/CustomProviderUpdateRequest.tokenUrl|{}tokenUrl[0] final fun (): kotlin/String? // io.github.androidpoet.supabase.auth.admin.models/CustomProviderUpdateRequest.tokenUrl.|(){}[0] - final val userinfoUrl // io.github.androidpoet.supabase.auth.admin.models/CustomProviderUpdateRequest.userinfoUrl|{}userinfoUrl[0] - final fun (): kotlin/String? // io.github.androidpoet.supabase.auth.admin.models/CustomProviderUpdateRequest.userinfoUrl.|(){}[0] + final val userInfoUrl // io.github.androidpoet.supabase.auth.admin.models/CustomProviderUpdateRequest.userInfoUrl|{}userInfoUrl[0] + final fun (): kotlin/String? // io.github.androidpoet.supabase.auth.admin.models/CustomProviderUpdateRequest.userInfoUrl.|(){}[0] final fun component1(): kotlin/String? // io.github.androidpoet.supabase.auth.admin.models/CustomProviderUpdateRequest.component1|component1(){}[0] final fun component10(): kotlin/Boolean? // io.github.androidpoet.supabase.auth.admin.models/CustomProviderUpdateRequest.component10|component10(){}[0] diff --git a/supabase-auth-admin/src/commonMain/kotlin/io/github/androidpoet/supabase/auth/admin/AuthAdminClient.kt b/supabase-auth-admin/src/commonMain/kotlin/io/github/androidpoet/supabase/auth/admin/AuthAdminClient.kt index ea4ee1f..df829eb 100644 --- a/supabase-auth-admin/src/commonMain/kotlin/io/github/androidpoet/supabase/auth/admin/AuthAdminClient.kt +++ b/supabase-auth-admin/src/commonMain/kotlin/io/github/androidpoet/supabase/auth/admin/AuthAdminClient.kt @@ -1,7 +1,7 @@ package io.github.androidpoet.supabase.auth.admin import io.github.androidpoet.supabase.auth.admin.models.AdminUserAttributes -import io.github.androidpoet.supabase.auth.admin.models.AuditLogEntry +import io.github.androidpoet.supabase.auth.admin.models.AuditLogEvent import io.github.androidpoet.supabase.auth.admin.models.CustomProvider import io.github.androidpoet.supabase.auth.admin.models.CustomProviderCreateRequest import io.github.androidpoet.supabase.auth.admin.models.CustomProviderListResponse @@ -150,13 +150,13 @@ public interface AuthAdminClient { /** * Fetches audit-log events. * - * Requires the service-role key. The endpoint returns a bare JSON array of [AuditLogEntry]. + * Requires the service-role key. The endpoint returns a bare JSON array of [AuditLogEvent]. * [page] and [perPage] are sent as `page` / `per_page` query params only when non-null. */ public suspend fun listAuditLogEvents( page: Int? = null, perPage: Int? = null, - ): SupabaseResult> + ): SupabaseResult> public suspend fun listPasskeys(userId: String): SupabaseResult> diff --git a/supabase-auth-admin/src/commonMain/kotlin/io/github/androidpoet/supabase/auth/admin/AuthAdminClientImpl.kt b/supabase-auth-admin/src/commonMain/kotlin/io/github/androidpoet/supabase/auth/admin/AuthAdminClientImpl.kt index 588f12b..c1de5b9 100644 --- a/supabase-auth-admin/src/commonMain/kotlin/io/github/androidpoet/supabase/auth/admin/AuthAdminClientImpl.kt +++ b/supabase-auth-admin/src/commonMain/kotlin/io/github/androidpoet/supabase/auth/admin/AuthAdminClientImpl.kt @@ -1,7 +1,7 @@ package io.github.androidpoet.supabase.auth.admin import io.github.androidpoet.supabase.auth.admin.models.AdminUserAttributes -import io.github.androidpoet.supabase.auth.admin.models.AuditLogEntry +import io.github.androidpoet.supabase.auth.admin.models.AuditLogEvent import io.github.androidpoet.supabase.auth.admin.models.CustomProvider import io.github.androidpoet.supabase.auth.admin.models.CustomProviderCreateRequest import io.github.androidpoet.supabase.auth.admin.models.CustomProviderListResponse @@ -195,7 +195,7 @@ internal class AuthAdminClientImpl( override suspend fun listAuditLogEvents( page: Int?, perPage: Int?, - ): SupabaseResult> { + ): SupabaseResult> { val queryParams = buildList { if (page != null) add("page" to page.toString()) diff --git a/supabase-auth-admin/src/commonMain/kotlin/io/github/androidpoet/supabase/auth/admin/models/AuthAdminModels.kt b/supabase-auth-admin/src/commonMain/kotlin/io/github/androidpoet/supabase/auth/admin/models/AuthAdminModels.kt index 6ff808e..663081b 100644 --- a/supabase-auth-admin/src/commonMain/kotlin/io/github/androidpoet/supabase/auth/admin/models/AuthAdminModels.kt +++ b/supabase-auth-admin/src/commonMain/kotlin/io/github/androidpoet/supabase/auth/admin/models/AuthAdminModels.kt @@ -247,7 +247,7 @@ public data class CustomProvider( @SerialName("skip_nonce_check") public val skipNonceCheck: Boolean? = null, @SerialName("authorization_url") public val authorizationUrl: String? = null, @SerialName("token_url") public val tokenUrl: String? = null, - @SerialName("userinfo_url") public val userinfoUrl: String? = null, + @SerialName("userinfo_url") public val userInfoUrl: String? = null, @SerialName("jwks_uri") public val jwksUrl: String? = null, @SerialName("discovery_document") public val discoveryDocument: OidcDiscoveryDocument? = null, @SerialName("created_at") public val createdAt: String? = null, @@ -273,7 +273,7 @@ public data class CustomProviderCreateRequest( @SerialName("skip_nonce_check") public val skipNonceCheck: Boolean? = null, @SerialName("authorization_url") public val authorizationUrl: String? = null, @SerialName("token_url") public val tokenUrl: String? = null, - @SerialName("userinfo_url") public val userinfoUrl: String? = null, + @SerialName("userinfo_url") public val userInfoUrl: String? = null, @SerialName("jwks_uri") public val jwksUrl: String? = null, ) { // Mask the client secret so it never leaks into logs or crash reports. @@ -283,7 +283,7 @@ public data class CustomProviderCreateRequest( "scopes=$scopes, pkceEnabled=$pkceEnabled, attributeMapping=$attributeMapping, " + "authorizationParams=$authorizationParams, enabled=$enabled, emailOptional=$emailOptional, " + "issuer=$issuer, discoveryUrl=$discoveryUrl, skipNonceCheck=$skipNonceCheck, " + - "authorizationUrl=$authorizationUrl, tokenUrl=$tokenUrl, userinfoUrl=$userinfoUrl, " + + "authorizationUrl=$authorizationUrl, tokenUrl=$tokenUrl, userInfoUrl=$userInfoUrl, " + "jwksUrl=$jwksUrl)" } @@ -304,7 +304,7 @@ public data class CustomProviderUpdateRequest( @SerialName("skip_nonce_check") public val skipNonceCheck: Boolean? = null, @SerialName("authorization_url") public val authorizationUrl: String? = null, @SerialName("token_url") public val tokenUrl: String? = null, - @SerialName("userinfo_url") public val userinfoUrl: String? = null, + @SerialName("userinfo_url") public val userInfoUrl: String? = null, @SerialName("jwks_uri") public val jwksUrl: String? = null, ) { // Mask the client secret so it never leaks into logs or crash reports. @@ -315,7 +315,7 @@ public data class CustomProviderUpdateRequest( "attributeMapping=$attributeMapping, authorizationParams=$authorizationParams, " + "enabled=$enabled, emailOptional=$emailOptional, issuer=$issuer, " + "discoveryUrl=$discoveryUrl, skipNonceCheck=$skipNonceCheck, " + - "authorizationUrl=$authorizationUrl, tokenUrl=$tokenUrl, userinfoUrl=$userinfoUrl, " + + "authorizationUrl=$authorizationUrl, tokenUrl=$tokenUrl, userInfoUrl=$userInfoUrl, " + "jwksUrl=$jwksUrl)" } @@ -398,7 +398,7 @@ public data class Passkey( * so it is kept as a raw [JsonObject] rather than a strongly typed model. */ @Serializable -public data class AuditLogEntry( +public data class AuditLogEvent( public val id: String, public val payload: JsonObject? = null, @SerialName("created_at") public val createdAt: String? = null, diff --git a/supabase-auth-admin/src/commonTest/kotlin/io/github/androidpoet/supabase/auth/admin/AuthAdminClientImplTest.kt b/supabase-auth-admin/src/commonTest/kotlin/io/github/androidpoet/supabase/auth/admin/AuthAdminClientImplTest.kt index 4eea2a3..67785b0 100644 --- a/supabase-auth-admin/src/commonTest/kotlin/io/github/androidpoet/supabase/auth/admin/AuthAdminClientImplTest.kt +++ b/supabase-auth-admin/src/commonTest/kotlin/io/github/androidpoet/supabase/auth/admin/AuthAdminClientImplTest.kt @@ -185,7 +185,7 @@ class AuthAdminClientImplTest { assertEquals(listOf("page" to "2", "per_page" to "50"), client.lastGetQueryParams) assertEquals("Bearer service-role", client.lastGetHeaders["Authorization"]) val value = success.value as List<*> - val entry = value.first() as io.github.androidpoet.supabase.auth.admin.models.AuditLogEntry + val entry = value.first() as io.github.androidpoet.supabase.auth.admin.models.AuditLogEvent assertEquals("audit1", entry.id) assertEquals("203.0.113.7", entry.ipAddress) assertEquals( diff --git a/supabase-auth/src/commonMain/kotlin/io/github/androidpoet/supabase/auth/session/SessionManagerImpl.kt b/supabase-auth/src/commonMain/kotlin/io/github/androidpoet/supabase/auth/session/SessionManagerImpl.kt index 8a190e2..e21208e 100644 --- a/supabase-auth/src/commonMain/kotlin/io/github/androidpoet/supabase/auth/session/SessionManagerImpl.kt +++ b/supabase-auth/src/commonMain/kotlin/io/github/androidpoet/supabase/auth/session/SessionManagerImpl.kt @@ -144,9 +144,9 @@ internal class SessionManagerImpl( val transient = result.error.category in setOf( - SupabaseErrorCategory.Network, - SupabaseErrorCategory.Internal, - SupabaseErrorCategory.RateLimited, + SupabaseErrorCategory.NETWORK, + SupabaseErrorCategory.INTERNAL, + SupabaseErrorCategory.RATE_LIMITED, ) if (transient) { // A transient failure (offline, 5xx, 429) leaves the refresh token diff --git a/supabase-client/api/android/supabase-client.api b/supabase-client/api/android/supabase-client.api index 3b11d24..39b95e0 100644 --- a/supabase-client/api/android/supabase-client.api +++ b/supabase-client/api/android/supabase-client.api @@ -97,8 +97,8 @@ public final class io/github/androidpoet/supabase/client/SupabaseConfig { public final fun getConnectTimeoutMillis ()Ljava/lang/Long; public final fun getHeaders ()Ljava/util/Map; public final fun getHttpClientConfig ()Lkotlin/jvm/functions/Function1; + public final fun getHttpLogLevel ()Lio/github/androidpoet/supabase/client/HttpLogLevel; public final fun getInterceptor ()Lio/github/androidpoet/supabase/client/SupabaseInterceptor; - public final fun getLogLevel ()Lio/github/androidpoet/supabase/client/HttpLogLevel; public final fun getLogger ()Lio/github/androidpoet/supabase/client/SupabaseLogger; public final fun getLogging ()Z public final fun getRequestTimeoutMillis ()Ljava/lang/Long; @@ -112,8 +112,8 @@ public final class io/github/androidpoet/supabase/client/SupabaseConfigBuilder { public final fun getConnectTimeoutMillis ()Ljava/lang/Long; public final fun getHeaders ()Ljava/util/Map; public final fun getHttpClientConfig ()Lkotlin/jvm/functions/Function1; + public final fun getHttpLogLevel ()Lio/github/androidpoet/supabase/client/HttpLogLevel; public final fun getInterceptor ()Lio/github/androidpoet/supabase/client/SupabaseInterceptor; - public final fun getLogLevel ()Lio/github/androidpoet/supabase/client/HttpLogLevel; public final fun getLogger ()Lio/github/androidpoet/supabase/client/SupabaseLogger; public final fun getLogging ()Z public final fun getRequestTimeoutMillis ()Ljava/lang/Long; @@ -122,8 +122,8 @@ public final class io/github/androidpoet/supabase/client/SupabaseConfigBuilder { public final fun setAccessTokenProvider (Lkotlin/jvm/functions/Function1;)V public final fun setConnectTimeoutMillis (Ljava/lang/Long;)V public final fun setHttpClientConfig (Lkotlin/jvm/functions/Function1;)V + public final fun setHttpLogLevel (Lio/github/androidpoet/supabase/client/HttpLogLevel;)V public final fun setInterceptor (Lio/github/androidpoet/supabase/client/SupabaseInterceptor;)V - public final fun setLogLevel (Lio/github/androidpoet/supabase/client/HttpLogLevel;)V public final fun setLogger (Lio/github/androidpoet/supabase/client/SupabaseLogger;)V public final fun setLogging (Z)V public final fun setRequestTimeoutMillis (Ljava/lang/Long;)V diff --git a/supabase-client/api/jvm/supabase-client.api b/supabase-client/api/jvm/supabase-client.api index 3b11d24..39b95e0 100644 --- a/supabase-client/api/jvm/supabase-client.api +++ b/supabase-client/api/jvm/supabase-client.api @@ -97,8 +97,8 @@ public final class io/github/androidpoet/supabase/client/SupabaseConfig { public final fun getConnectTimeoutMillis ()Ljava/lang/Long; public final fun getHeaders ()Ljava/util/Map; public final fun getHttpClientConfig ()Lkotlin/jvm/functions/Function1; + public final fun getHttpLogLevel ()Lio/github/androidpoet/supabase/client/HttpLogLevel; public final fun getInterceptor ()Lio/github/androidpoet/supabase/client/SupabaseInterceptor; - public final fun getLogLevel ()Lio/github/androidpoet/supabase/client/HttpLogLevel; public final fun getLogger ()Lio/github/androidpoet/supabase/client/SupabaseLogger; public final fun getLogging ()Z public final fun getRequestTimeoutMillis ()Ljava/lang/Long; @@ -112,8 +112,8 @@ public final class io/github/androidpoet/supabase/client/SupabaseConfigBuilder { public final fun getConnectTimeoutMillis ()Ljava/lang/Long; public final fun getHeaders ()Ljava/util/Map; public final fun getHttpClientConfig ()Lkotlin/jvm/functions/Function1; + public final fun getHttpLogLevel ()Lio/github/androidpoet/supabase/client/HttpLogLevel; public final fun getInterceptor ()Lio/github/androidpoet/supabase/client/SupabaseInterceptor; - public final fun getLogLevel ()Lio/github/androidpoet/supabase/client/HttpLogLevel; public final fun getLogger ()Lio/github/androidpoet/supabase/client/SupabaseLogger; public final fun getLogging ()Z public final fun getRequestTimeoutMillis ()Ljava/lang/Long; @@ -122,8 +122,8 @@ public final class io/github/androidpoet/supabase/client/SupabaseConfigBuilder { public final fun setAccessTokenProvider (Lkotlin/jvm/functions/Function1;)V public final fun setConnectTimeoutMillis (Ljava/lang/Long;)V public final fun setHttpClientConfig (Lkotlin/jvm/functions/Function1;)V + public final fun setHttpLogLevel (Lio/github/androidpoet/supabase/client/HttpLogLevel;)V public final fun setInterceptor (Lio/github/androidpoet/supabase/client/SupabaseInterceptor;)V - public final fun setLogLevel (Lio/github/androidpoet/supabase/client/HttpLogLevel;)V public final fun setLogger (Lio/github/androidpoet/supabase/client/SupabaseLogger;)V public final fun setLogging (Z)V public final fun setRequestTimeoutMillis (Ljava/lang/Long;)V diff --git a/supabase-client/api/supabase-client.klib.api b/supabase-client/api/supabase-client.klib.api index 7b126e0..8edd5c6 100644 --- a/supabase-client/api/supabase-client.klib.api +++ b/supabase-client/api/supabase-client.klib.api @@ -169,10 +169,10 @@ final class io.github.androidpoet.supabase.client/SupabaseConfig { // io.github. final fun (): kotlin.collections/Map // io.github.androidpoet.supabase.client/SupabaseConfig.headers.|(){}[0] final val httpClientConfig // io.github.androidpoet.supabase.client/SupabaseConfig.httpClientConfig|{}httpClientConfig[0] final fun (): kotlin/Function1, kotlin/Unit>? // io.github.androidpoet.supabase.client/SupabaseConfig.httpClientConfig.|(){}[0] + final val httpLogLevel // io.github.androidpoet.supabase.client/SupabaseConfig.httpLogLevel|{}httpLogLevel[0] + final fun (): io.github.androidpoet.supabase.client/HttpLogLevel // io.github.androidpoet.supabase.client/SupabaseConfig.httpLogLevel.|(){}[0] final val interceptor // io.github.androidpoet.supabase.client/SupabaseConfig.interceptor|{}interceptor[0] final fun (): io.github.androidpoet.supabase.client/SupabaseInterceptor? // io.github.androidpoet.supabase.client/SupabaseConfig.interceptor.|(){}[0] - final val logLevel // io.github.androidpoet.supabase.client/SupabaseConfig.logLevel|{}logLevel[0] - final fun (): io.github.androidpoet.supabase.client/HttpLogLevel // io.github.androidpoet.supabase.client/SupabaseConfig.logLevel.|(){}[0] final val logger // io.github.androidpoet.supabase.client/SupabaseConfig.logger|{}logger[0] final fun (): io.github.androidpoet.supabase.client/SupabaseLogger? // io.github.androidpoet.supabase.client/SupabaseConfig.logger.|(){}[0] final val logging // io.github.androidpoet.supabase.client/SupabaseConfig.logging|{}logging[0] @@ -200,12 +200,12 @@ final class io.github.androidpoet.supabase.client/SupabaseConfigBuilder { // io. final var httpClientConfig // io.github.androidpoet.supabase.client/SupabaseConfigBuilder.httpClientConfig|{}httpClientConfig[0] final fun (): kotlin/Function1, kotlin/Unit>? // io.github.androidpoet.supabase.client/SupabaseConfigBuilder.httpClientConfig.|(){}[0] final fun (kotlin/Function1, kotlin/Unit>?) // io.github.androidpoet.supabase.client/SupabaseConfigBuilder.httpClientConfig.|(kotlin.Function1,kotlin.Unit>?){}[0] + final var httpLogLevel // io.github.androidpoet.supabase.client/SupabaseConfigBuilder.httpLogLevel|{}httpLogLevel[0] + final fun (): io.github.androidpoet.supabase.client/HttpLogLevel // io.github.androidpoet.supabase.client/SupabaseConfigBuilder.httpLogLevel.|(){}[0] + final fun (io.github.androidpoet.supabase.client/HttpLogLevel) // io.github.androidpoet.supabase.client/SupabaseConfigBuilder.httpLogLevel.|(io.github.androidpoet.supabase.client.HttpLogLevel){}[0] final var interceptor // io.github.androidpoet.supabase.client/SupabaseConfigBuilder.interceptor|{}interceptor[0] final fun (): io.github.androidpoet.supabase.client/SupabaseInterceptor? // io.github.androidpoet.supabase.client/SupabaseConfigBuilder.interceptor.|(){}[0] final fun (io.github.androidpoet.supabase.client/SupabaseInterceptor?) // io.github.androidpoet.supabase.client/SupabaseConfigBuilder.interceptor.|(io.github.androidpoet.supabase.client.SupabaseInterceptor?){}[0] - final var logLevel // io.github.androidpoet.supabase.client/SupabaseConfigBuilder.logLevel|{}logLevel[0] - final fun (): io.github.androidpoet.supabase.client/HttpLogLevel // io.github.androidpoet.supabase.client/SupabaseConfigBuilder.logLevel.|(){}[0] - final fun (io.github.androidpoet.supabase.client/HttpLogLevel) // io.github.androidpoet.supabase.client/SupabaseConfigBuilder.logLevel.|(io.github.androidpoet.supabase.client.HttpLogLevel){}[0] final var logger // io.github.androidpoet.supabase.client/SupabaseConfigBuilder.logger|{}logger[0] final fun (): io.github.androidpoet.supabase.client/SupabaseLogger? // io.github.androidpoet.supabase.client/SupabaseConfigBuilder.logger.|(){}[0] final fun (io.github.androidpoet.supabase.client/SupabaseLogger?) // io.github.androidpoet.supabase.client/SupabaseConfigBuilder.logger.|(io.github.androidpoet.supabase.client.SupabaseLogger?){}[0] diff --git a/supabase-client/src/commonMain/kotlin/io/github/androidpoet/supabase/client/SupabaseConfig.kt b/supabase-client/src/commonMain/kotlin/io/github/androidpoet/supabase/client/SupabaseConfig.kt index 8363505..61a9fc5 100644 --- a/supabase-client/src/commonMain/kotlin/io/github/androidpoet/supabase/client/SupabaseConfig.kt +++ b/supabase-client/src/commonMain/kotlin/io/github/androidpoet/supabase/client/SupabaseConfig.kt @@ -30,11 +30,11 @@ public enum class HttpLogLevel { */ @SupabaseDsl public class SupabaseConfigBuilder { - /** Enable HTTP wire logging at [logLevel]. Off by default. */ + /** Enable HTTP wire logging at [httpLogLevel]. Off by default. */ public var logging: Boolean = false /** Verbosity of the wire log when [logging] is on. */ - public var logLevel: HttpLogLevel = HttpLogLevel.NONE + public var httpLogLevel: HttpLogLevel = HttpLogLevel.NONE /** Extra headers attached to every request, merged under any per-call headers. */ public val headers: MutableMap = mutableMapOf() @@ -98,7 +98,7 @@ public class SupabaseConfigBuilder { internal fun build(): SupabaseConfig = SupabaseConfig( logging = logging, - logLevel = logLevel, + httpLogLevel = httpLogLevel, headers = headers.toMap(), retry = retry, logger = logger, @@ -127,8 +127,8 @@ public class SupabaseConfigBuilder { public class SupabaseConfig( /** Whether HTTP wire logging is enabled. See [SupabaseConfigBuilder.logging]. */ public val logging: Boolean, - /** Verbosity of the wire log. See [SupabaseConfigBuilder.logLevel]. */ - public val logLevel: HttpLogLevel, + /** Verbosity of the wire log. See [SupabaseConfigBuilder.httpLogLevel]. */ + public val httpLogLevel: HttpLogLevel, /** Extra headers attached to every request. See [SupabaseConfigBuilder.headers]. */ public val headers: Map, /** Retry policy for transient failures. See [RetryConfig]. */ diff --git a/supabase-client/src/commonMain/kotlin/io/github/androidpoet/supabase/client/SupabaseLogger.kt b/supabase-client/src/commonMain/kotlin/io/github/androidpoet/supabase/client/SupabaseLogger.kt index fa0f059..b3bad18 100644 --- a/supabase-client/src/commonMain/kotlin/io/github/androidpoet/supabase/client/SupabaseLogger.kt +++ b/supabase-client/src/commonMain/kotlin/io/github/androidpoet/supabase/client/SupabaseLogger.kt @@ -9,7 +9,7 @@ public enum class SupabaseLogLevel { DEBUG, INFO, WARN, ERROR } * Provide one via `SupabaseConfigBuilder.logger` to route HTTP wire logs into * your app's logging framework (Timber, OSLog, SLF4J, …) instead of relying on * Ktor's default `println`-style logger. Verbosity is still governed by - * `logLevel`; this only changes where the lines go. + * `httpLogLevel`; this only changes where the lines go. */ public interface SupabaseLogger { public fun log( diff --git a/supabase-client/src/commonMain/kotlin/io/github/androidpoet/supabase/client/transport/HttpTransport.kt b/supabase-client/src/commonMain/kotlin/io/github/androidpoet/supabase/client/transport/HttpTransport.kt index 44dd113..23ff0df 100644 --- a/supabase-client/src/commonMain/kotlin/io/github/androidpoet/supabase/client/transport/HttpTransport.kt +++ b/supabase-client/src/commonMain/kotlin/io/github/androidpoet/supabase/client/transport/HttpTransport.kt @@ -121,7 +121,7 @@ internal class HttpTransport( } if (config.logging) { install(Logging) { - level = config.logLevel.toKtorLogLevel() + level = config.httpLogLevel.toKtorLogLevel() // Route wire logs into the caller's framework when a logger is supplied; // otherwise fall back to Ktor's default sink. config.logger?.let { sink -> @@ -494,7 +494,7 @@ internal class HttpTransport( // A request that throws (rather than returning a response) never reached a // usable server response: offline, DNS/TLS failure, connection refused, or a // timeout. Tag it with a synthetic [SupabaseErrorCodes.Client] code so - // callers see [SupabaseErrorCategory.Network] instead of Unknown. + // callers see [SupabaseErrorCategory.NETWORK] instead of Unknown. private fun networkError( throwable: Throwable? = null, message: String? = null, diff --git a/supabase-client/src/commonTest/kotlin/io/github/androidpoet/supabase/client/transport/ErrorParsingConformanceTest.kt b/supabase-client/src/commonTest/kotlin/io/github/androidpoet/supabase/client/transport/ErrorParsingConformanceTest.kt index 8e7d8bf..cd24ee8 100644 --- a/supabase-client/src/commonTest/kotlin/io/github/androidpoet/supabase/client/transport/ErrorParsingConformanceTest.kt +++ b/supabase-client/src/commonTest/kotlin/io/github/androidpoet/supabase/client/transport/ErrorParsingConformanceTest.kt @@ -39,7 +39,7 @@ class ErrorParsingConformanceTest { headersOf("Content-Type", "application/json") } return HttpTransport( - config = SupabaseConfig(logging = false, logLevel = io.github.androidpoet.supabase.client.HttpLogLevel.NONE, headers = emptyMap()), + config = SupabaseConfig(logging = false, httpLogLevel = io.github.androidpoet.supabase.client.HttpLogLevel.NONE, headers = emptyMap()), engineFactory = TestMockEngineFactory { respond(content = body, status = status, headers = headers) }, projectUrl = "https://example.supabase.co", apiKey = "anon", @@ -63,7 +63,7 @@ class ErrorParsingConformanceTest { assertEquals("23505", error.code) assertEquals("duplicate key violates unique constraint", error.message) assertEquals(409, error.httpStatus) - assertEquals(SupabaseErrorCategory.Conflict, error.category) + assertEquals(SupabaseErrorCategory.CONFLICT, error.category) } @Test @@ -79,7 +79,7 @@ class ErrorParsingConformanceTest { assertEquals("weak_password", error.code) assertEquals("Password is too weak", error.message) assertEquals(422, error.httpStatus) - assertEquals(SupabaseErrorCategory.Validation, error.category) + assertEquals(SupabaseErrorCategory.VALIDATION, error.category) } @Test @@ -92,7 +92,7 @@ class ErrorParsingConformanceTest { ) assertEquals("weak_password", error.code) assertEquals("Password should be at least 6 characters", error.message) - assertEquals(SupabaseErrorCategory.Validation, error.category) + assertEquals(SupabaseErrorCategory.VALIDATION, error.category) } @Test @@ -105,7 +105,7 @@ class ErrorParsingConformanceTest { """{"error":"invalid_grant","error_description":"Invalid login credentials"}""", ) assertEquals("Invalid login credentials", error.message) - assertEquals(SupabaseErrorCategory.Validation, error.category) + assertEquals(SupabaseErrorCategory.VALIDATION, error.category) } @Test @@ -118,7 +118,7 @@ class ErrorParsingConformanceTest { ) assertEquals("NoSuchKey", error.code) assertEquals("Object not found", error.message) - assertEquals(SupabaseErrorCategory.NotFound, error.category) + assertEquals(SupabaseErrorCategory.NOT_FOUND, error.category) } @Test @@ -136,7 +136,7 @@ class ErrorParsingConformanceTest { assertEquals("NoSuchKey", error.code) assertEquals("Object not found", error.message) assertTrue(error.isFileNotFound()) - assertEquals(SupabaseErrorCategory.NotFound, error.category) + assertEquals(SupabaseErrorCategory.NOT_FOUND, error.category) } @Test @@ -145,7 +145,7 @@ class ErrorParsingConformanceTest { // 408 is transient: it must be retryable and must NOT collapse to Unknown, // or the session transient-failure guard could sign the user out on a fluke. val error = errorFor(HttpStatusCode.RequestTimeout, "") - assertEquals(SupabaseErrorCategory.Internal, error.category) + assertEquals(SupabaseErrorCategory.INTERNAL, error.category) assertTrue(error.category.isRetryable) } @@ -159,7 +159,7 @@ class ErrorParsingConformanceTest { retryAfter = "30", ) assertEquals(30, error.retryAfterSeconds) - assertEquals(SupabaseErrorCategory.RateLimited, error.category) + assertEquals(SupabaseErrorCategory.RATE_LIMITED, error.category) assertTrue(error.category.isRetryable) } @@ -168,7 +168,7 @@ class ErrorParsingConformanceTest { runTest { val error = errorFor(HttpStatusCode.Unauthorized, """{"message":"JWT expired"}""") assertEquals(401, error.httpStatus) - assertEquals(SupabaseErrorCategory.Unauthorized, error.category) + assertEquals(SupabaseErrorCategory.UNAUTHORIZED, error.category) } @Test @@ -177,7 +177,7 @@ class ErrorParsingConformanceTest { val error = errorFor(HttpStatusCode.InternalServerError, "") assertEquals("HTTP 500", error.message) assertEquals(500, error.httpStatus) - assertEquals(SupabaseErrorCategory.Internal, error.category) + assertEquals(SupabaseErrorCategory.INTERNAL, error.category) assertTrue(error.category.isRetryable) } @@ -187,6 +187,6 @@ class ErrorParsingConformanceTest { val error = errorFor(HttpStatusCode.BadGateway, "upstream connection error") assertEquals("upstream connection error", error.message) assertEquals(502, error.httpStatus) - assertEquals(SupabaseErrorCategory.Internal, error.category) + assertEquals(SupabaseErrorCategory.INTERNAL, error.category) } } diff --git a/supabase-client/src/commonTest/kotlin/io/github/androidpoet/supabase/client/transport/HttpTransportAuthHeaderTest.kt b/supabase-client/src/commonTest/kotlin/io/github/androidpoet/supabase/client/transport/HttpTransportAuthHeaderTest.kt index e5e2f80..0685806 100644 --- a/supabase-client/src/commonTest/kotlin/io/github/androidpoet/supabase/client/transport/HttpTransportAuthHeaderTest.kt +++ b/supabase-client/src/commonTest/kotlin/io/github/androidpoet/supabase/client/transport/HttpTransportAuthHeaderTest.kt @@ -17,7 +17,7 @@ private const val MODERN_PUBLISHABLE = "sb_publishable_abc123" private fun config(accessTokenProvider: (suspend () -> String?)? = null) = SupabaseConfig( logging = false, - logLevel = io.github.androidpoet.supabase.client.HttpLogLevel.NONE, + httpLogLevel = io.github.androidpoet.supabase.client.HttpLogLevel.NONE, headers = emptyMap(), accessTokenProvider = accessTokenProvider, ) diff --git a/supabase-client/src/commonTest/kotlin/io/github/androidpoet/supabase/client/transport/HttpTransportClientConfigTest.kt b/supabase-client/src/commonTest/kotlin/io/github/androidpoet/supabase/client/transport/HttpTransportClientConfigTest.kt index 79b170e..bccd2ac 100644 --- a/supabase-client/src/commonTest/kotlin/io/github/androidpoet/supabase/client/transport/HttpTransportClientConfigTest.kt +++ b/supabase-client/src/commonTest/kotlin/io/github/androidpoet/supabase/client/transport/HttpTransportClientConfigTest.kt @@ -21,7 +21,7 @@ private fun transport( config = SupabaseConfig( logging = false, - logLevel = io.github.androidpoet.supabase.client.HttpLogLevel.NONE, + httpLogLevel = io.github.androidpoet.supabase.client.HttpLogLevel.NONE, headers = emptyMap(), httpClientConfig = httpClientConfig, ), diff --git a/supabase-client/src/commonTest/kotlin/io/github/androidpoet/supabase/client/transport/HttpTransportObservabilityTest.kt b/supabase-client/src/commonTest/kotlin/io/github/androidpoet/supabase/client/transport/HttpTransportObservabilityTest.kt index 451c6f7..1497bd8 100644 --- a/supabase-client/src/commonTest/kotlin/io/github/androidpoet/supabase/client/transport/HttpTransportObservabilityTest.kt +++ b/supabase-client/src/commonTest/kotlin/io/github/androidpoet/supabase/client/transport/HttpTransportObservabilityTest.kt @@ -44,10 +44,10 @@ private fun config( interceptor: SupabaseInterceptor? = null, logger: SupabaseLogger? = null, logging: Boolean = false, - logLevel: HttpLogLevel = HttpLogLevel.NONE, + httpLogLevel: HttpLogLevel = HttpLogLevel.NONE, ) = SupabaseConfig( logging = logging, - logLevel = logLevel, + httpLogLevel = httpLogLevel, headers = emptyMap(), retry = retry, logger = logger, @@ -167,7 +167,7 @@ class HttpTransportObservabilityTest { } } val transport = - transport(config(logger = sink, logging = true, logLevel = HttpLogLevel.ALL)) { + transport(config(logger = sink, logging = true, httpLogLevel = HttpLogLevel.ALL)) { respond("{}", HttpStatusCode.OK, headersOf(HttpHeaders.ContentType, "application/json")) } diff --git a/supabase-client/src/commonTest/kotlin/io/github/androidpoet/supabase/client/transport/HttpTransportQueryEncodingTest.kt b/supabase-client/src/commonTest/kotlin/io/github/androidpoet/supabase/client/transport/HttpTransportQueryEncodingTest.kt index 2beb0d7..388a5be 100644 --- a/supabase-client/src/commonTest/kotlin/io/github/androidpoet/supabase/client/transport/HttpTransportQueryEncodingTest.kt +++ b/supabase-client/src/commonTest/kotlin/io/github/androidpoet/supabase/client/transport/HttpTransportQueryEncodingTest.kt @@ -19,7 +19,7 @@ private fun transport(captured: MutableList) = config = SupabaseConfig( logging = false, - logLevel = io.github.androidpoet.supabase.client.HttpLogLevel.NONE, + httpLogLevel = io.github.androidpoet.supabase.client.HttpLogLevel.NONE, headers = emptyMap(), accessTokenProvider = null, ), diff --git a/supabase-client/src/commonTest/kotlin/io/github/androidpoet/supabase/client/transport/HttpTransportTest.kt b/supabase-client/src/commonTest/kotlin/io/github/androidpoet/supabase/client/transport/HttpTransportTest.kt index d9698b6..4ba8d5d 100644 --- a/supabase-client/src/commonTest/kotlin/io/github/androidpoet/supabase/client/transport/HttpTransportTest.kt +++ b/supabase-client/src/commonTest/kotlin/io/github/androidpoet/supabase/client/transport/HttpTransportTest.kt @@ -50,7 +50,7 @@ class HttpTransportTest { config = SupabaseConfig( logging = false, - logLevel = io.github.androidpoet.supabase.client.HttpLogLevel.NONE, + httpLogLevel = io.github.androidpoet.supabase.client.HttpLogLevel.NONE, headers = mapOf( "X-Trace-Id" to "global-trace", @@ -80,7 +80,7 @@ class HttpTransportTest { config = SupabaseConfig( logging = false, - logLevel = io.github.androidpoet.supabase.client.HttpLogLevel.NONE, + httpLogLevel = io.github.androidpoet.supabase.client.HttpLogLevel.NONE, headers = emptyMap(), ), engineFactory = @@ -117,7 +117,7 @@ class HttpTransportTest { config = SupabaseConfig( logging = false, - logLevel = io.github.androidpoet.supabase.client.HttpLogLevel.NONE, + httpLogLevel = io.github.androidpoet.supabase.client.HttpLogLevel.NONE, headers = emptyMap(), ), engineFactory = @@ -150,7 +150,7 @@ class HttpTransportTest { config = SupabaseConfig( logging = false, - logLevel = io.github.androidpoet.supabase.client.HttpLogLevel.NONE, + httpLogLevel = io.github.androidpoet.supabase.client.HttpLogLevel.NONE, headers = emptyMap(), ), engineFactory = @@ -181,7 +181,7 @@ class HttpTransportTest { config = SupabaseConfig( logging = false, - logLevel = io.github.androidpoet.supabase.client.HttpLogLevel.NONE, + httpLogLevel = io.github.androidpoet.supabase.client.HttpLogLevel.NONE, headers = emptyMap(), retry = io.github.androidpoet.supabase.client @@ -231,7 +231,7 @@ class HttpTransportTest { config = SupabaseConfig( logging = false, - logLevel = io.github.androidpoet.supabase.client.HttpLogLevel.NONE, + httpLogLevel = io.github.androidpoet.supabase.client.HttpLogLevel.NONE, headers = emptyMap(), retry = io.github.androidpoet.supabase.client @@ -276,7 +276,7 @@ class HttpTransportTest { config = SupabaseConfig( logging = false, - logLevel = io.github.androidpoet.supabase.client.HttpLogLevel.NONE, + httpLogLevel = io.github.androidpoet.supabase.client.HttpLogLevel.NONE, headers = emptyMap(), ), engineFactory = diff --git a/supabase-core/api/android/supabase-core.api b/supabase-core/api/android/supabase-core.api index a470f8f..ed55bfe 100644 --- a/supabase-core/api/android/supabase-core.api +++ b/supabase-core/api/android/supabase-core.api @@ -83,10 +83,10 @@ public final class io/github/androidpoet/supabase/core/models/QueryBuilder { } public final class io/github/androidpoet/supabase/core/models/TextSearchType : java/lang/Enum { - public static final field Phrase Lio/github/androidpoet/supabase/core/models/TextSearchType; - public static final field Plain Lio/github/androidpoet/supabase/core/models/TextSearchType; - public static final field Raw Lio/github/androidpoet/supabase/core/models/TextSearchType; - public static final field Websearch Lio/github/androidpoet/supabase/core/models/TextSearchType; + public static final field PHRASE Lio/github/androidpoet/supabase/core/models/TextSearchType; + public static final field PLAIN Lio/github/androidpoet/supabase/core/models/TextSearchType; + public static final field RAW Lio/github/androidpoet/supabase/core/models/TextSearchType; + public static final field WEB_SEARCH Lio/github/androidpoet/supabase/core/models/TextSearchType; public static fun getEntries ()Lkotlin/enums/EnumEntries; public static fun valueOf (Ljava/lang/String;)Lio/github/androidpoet/supabase/core/models/TextSearchType; public static fun values ()[Lio/github/androidpoet/supabase/core/models/TextSearchType; @@ -183,14 +183,14 @@ public final class io/github/androidpoet/supabase/core/result/SupabaseError$Comp } public final class io/github/androidpoet/supabase/core/result/SupabaseErrorCategory : java/lang/Enum { - public static final field Conflict Lio/github/androidpoet/supabase/core/result/SupabaseErrorCategory; - public static final field Internal Lio/github/androidpoet/supabase/core/result/SupabaseErrorCategory; - public static final field Network Lio/github/androidpoet/supabase/core/result/SupabaseErrorCategory; - public static final field NotFound Lio/github/androidpoet/supabase/core/result/SupabaseErrorCategory; - public static final field RateLimited Lio/github/androidpoet/supabase/core/result/SupabaseErrorCategory; - public static final field Unauthorized Lio/github/androidpoet/supabase/core/result/SupabaseErrorCategory; - public static final field Unknown Lio/github/androidpoet/supabase/core/result/SupabaseErrorCategory; - public static final field Validation Lio/github/androidpoet/supabase/core/result/SupabaseErrorCategory; + public static final field CONFLICT Lio/github/androidpoet/supabase/core/result/SupabaseErrorCategory; + public static final field INTERNAL Lio/github/androidpoet/supabase/core/result/SupabaseErrorCategory; + public static final field NETWORK Lio/github/androidpoet/supabase/core/result/SupabaseErrorCategory; + public static final field NOT_FOUND Lio/github/androidpoet/supabase/core/result/SupabaseErrorCategory; + public static final field RATE_LIMITED Lio/github/androidpoet/supabase/core/result/SupabaseErrorCategory; + public static final field UNAUTHORIZED Lio/github/androidpoet/supabase/core/result/SupabaseErrorCategory; + public static final field UNKNOWN Lio/github/androidpoet/supabase/core/result/SupabaseErrorCategory; + public static final field VALIDATION Lio/github/androidpoet/supabase/core/result/SupabaseErrorCategory; public static fun getEntries ()Lkotlin/enums/EnumEntries; public static fun valueOf (Ljava/lang/String;)Lio/github/androidpoet/supabase/core/result/SupabaseErrorCategory; public static fun values ()[Lio/github/androidpoet/supabase/core/result/SupabaseErrorCategory; diff --git a/supabase-core/api/jvm/supabase-core.api b/supabase-core/api/jvm/supabase-core.api index a470f8f..ed55bfe 100644 --- a/supabase-core/api/jvm/supabase-core.api +++ b/supabase-core/api/jvm/supabase-core.api @@ -83,10 +83,10 @@ public final class io/github/androidpoet/supabase/core/models/QueryBuilder { } public final class io/github/androidpoet/supabase/core/models/TextSearchType : java/lang/Enum { - public static final field Phrase Lio/github/androidpoet/supabase/core/models/TextSearchType; - public static final field Plain Lio/github/androidpoet/supabase/core/models/TextSearchType; - public static final field Raw Lio/github/androidpoet/supabase/core/models/TextSearchType; - public static final field Websearch Lio/github/androidpoet/supabase/core/models/TextSearchType; + public static final field PHRASE Lio/github/androidpoet/supabase/core/models/TextSearchType; + public static final field PLAIN Lio/github/androidpoet/supabase/core/models/TextSearchType; + public static final field RAW Lio/github/androidpoet/supabase/core/models/TextSearchType; + public static final field WEB_SEARCH Lio/github/androidpoet/supabase/core/models/TextSearchType; public static fun getEntries ()Lkotlin/enums/EnumEntries; public static fun valueOf (Ljava/lang/String;)Lio/github/androidpoet/supabase/core/models/TextSearchType; public static fun values ()[Lio/github/androidpoet/supabase/core/models/TextSearchType; @@ -183,14 +183,14 @@ public final class io/github/androidpoet/supabase/core/result/SupabaseError$Comp } public final class io/github/androidpoet/supabase/core/result/SupabaseErrorCategory : java/lang/Enum { - public static final field Conflict Lio/github/androidpoet/supabase/core/result/SupabaseErrorCategory; - public static final field Internal Lio/github/androidpoet/supabase/core/result/SupabaseErrorCategory; - public static final field Network Lio/github/androidpoet/supabase/core/result/SupabaseErrorCategory; - public static final field NotFound Lio/github/androidpoet/supabase/core/result/SupabaseErrorCategory; - public static final field RateLimited Lio/github/androidpoet/supabase/core/result/SupabaseErrorCategory; - public static final field Unauthorized Lio/github/androidpoet/supabase/core/result/SupabaseErrorCategory; - public static final field Unknown Lio/github/androidpoet/supabase/core/result/SupabaseErrorCategory; - public static final field Validation Lio/github/androidpoet/supabase/core/result/SupabaseErrorCategory; + public static final field CONFLICT Lio/github/androidpoet/supabase/core/result/SupabaseErrorCategory; + public static final field INTERNAL Lio/github/androidpoet/supabase/core/result/SupabaseErrorCategory; + public static final field NETWORK Lio/github/androidpoet/supabase/core/result/SupabaseErrorCategory; + public static final field NOT_FOUND Lio/github/androidpoet/supabase/core/result/SupabaseErrorCategory; + public static final field RATE_LIMITED Lio/github/androidpoet/supabase/core/result/SupabaseErrorCategory; + public static final field UNAUTHORIZED Lio/github/androidpoet/supabase/core/result/SupabaseErrorCategory; + public static final field UNKNOWN Lio/github/androidpoet/supabase/core/result/SupabaseErrorCategory; + public static final field VALIDATION Lio/github/androidpoet/supabase/core/result/SupabaseErrorCategory; public static fun getEntries ()Lkotlin/enums/EnumEntries; public static fun valueOf (Ljava/lang/String;)Lio/github/androidpoet/supabase/core/result/SupabaseErrorCategory; public static fun values ()[Lio/github/androidpoet/supabase/core/result/SupabaseErrorCategory; diff --git a/supabase-core/api/supabase-core.klib.api b/supabase-core/api/supabase-core.klib.api index 7beb00d..b834932 100644 --- a/supabase-core/api/supabase-core.klib.api +++ b/supabase-core/api/supabase-core.klib.api @@ -33,10 +33,10 @@ final enum class io.github.androidpoet.supabase.core.models/Order : kotlin/Enum< } final enum class io.github.androidpoet.supabase.core.models/TextSearchType : kotlin/Enum { // io.github.androidpoet.supabase.core.models/TextSearchType|null[0] - enum entry Phrase // io.github.androidpoet.supabase.core.models/TextSearchType.Phrase|null[0] - enum entry Plain // io.github.androidpoet.supabase.core.models/TextSearchType.Plain|null[0] - enum entry Raw // io.github.androidpoet.supabase.core.models/TextSearchType.Raw|null[0] - enum entry Websearch // io.github.androidpoet.supabase.core.models/TextSearchType.Websearch|null[0] + enum entry PHRASE // io.github.androidpoet.supabase.core.models/TextSearchType.PHRASE|null[0] + enum entry PLAIN // io.github.androidpoet.supabase.core.models/TextSearchType.PLAIN|null[0] + enum entry RAW // io.github.androidpoet.supabase.core.models/TextSearchType.RAW|null[0] + enum entry WEB_SEARCH // io.github.androidpoet.supabase.core.models/TextSearchType.WEB_SEARCH|null[0] final val entries // io.github.androidpoet.supabase.core.models/TextSearchType.entries|#static{}entries[0] final fun (): kotlin.enums/EnumEntries // io.github.androidpoet.supabase.core.models/TextSearchType.entries.|#static(){}[0] @@ -46,14 +46,14 @@ final enum class io.github.androidpoet.supabase.core.models/TextSearchType : kot } final enum class io.github.androidpoet.supabase.core.result/SupabaseErrorCategory : kotlin/Enum { // io.github.androidpoet.supabase.core.result/SupabaseErrorCategory|null[0] - enum entry Conflict // io.github.androidpoet.supabase.core.result/SupabaseErrorCategory.Conflict|null[0] - enum entry Internal // io.github.androidpoet.supabase.core.result/SupabaseErrorCategory.Internal|null[0] - enum entry Network // io.github.androidpoet.supabase.core.result/SupabaseErrorCategory.Network|null[0] - enum entry NotFound // io.github.androidpoet.supabase.core.result/SupabaseErrorCategory.NotFound|null[0] - enum entry RateLimited // io.github.androidpoet.supabase.core.result/SupabaseErrorCategory.RateLimited|null[0] - enum entry Unauthorized // io.github.androidpoet.supabase.core.result/SupabaseErrorCategory.Unauthorized|null[0] - enum entry Unknown // io.github.androidpoet.supabase.core.result/SupabaseErrorCategory.Unknown|null[0] - enum entry Validation // io.github.androidpoet.supabase.core.result/SupabaseErrorCategory.Validation|null[0] + enum entry CONFLICT // io.github.androidpoet.supabase.core.result/SupabaseErrorCategory.CONFLICT|null[0] + enum entry INTERNAL // io.github.androidpoet.supabase.core.result/SupabaseErrorCategory.INTERNAL|null[0] + enum entry NETWORK // io.github.androidpoet.supabase.core.result/SupabaseErrorCategory.NETWORK|null[0] + enum entry NOT_FOUND // io.github.androidpoet.supabase.core.result/SupabaseErrorCategory.NOT_FOUND|null[0] + enum entry RATE_LIMITED // io.github.androidpoet.supabase.core.result/SupabaseErrorCategory.RATE_LIMITED|null[0] + enum entry UNAUTHORIZED // io.github.androidpoet.supabase.core.result/SupabaseErrorCategory.UNAUTHORIZED|null[0] + enum entry UNKNOWN // io.github.androidpoet.supabase.core.result/SupabaseErrorCategory.UNKNOWN|null[0] + enum entry VALIDATION // io.github.androidpoet.supabase.core.result/SupabaseErrorCategory.VALIDATION|null[0] final val entries // io.github.androidpoet.supabase.core.result/SupabaseErrorCategory.entries|#static{}entries[0] final fun (): kotlin.enums/EnumEntries // io.github.androidpoet.supabase.core.result/SupabaseErrorCategory.entries.|#static(){}[0] diff --git a/supabase-core/src/commonMain/kotlin/io/github/androidpoet/supabase/core/models/Filters.kt b/supabase-core/src/commonMain/kotlin/io/github/androidpoet/supabase/core/models/Filters.kt index 47454b9..97b6def 100644 --- a/supabase-core/src/commonMain/kotlin/io/github/androidpoet/supabase/core/models/Filters.kt +++ b/supabase-core/src/commonMain/kotlin/io/github/androidpoet/supabase/core/models/Filters.kt @@ -61,16 +61,16 @@ public enum class TextSearchType( internal val postgrestName: String, ) { /** Bare `fts` (passed straight to `to_tsquery`). */ - Raw(""), + RAW(""), /** Plain `plfts`. */ - Plain("pl"), + PLAIN("pl"), /** Phrase `phfts`. */ - Phrase("ph"), + PHRASE("ph"), /** Web-search `wfts`. */ - Websearch("w"), + WEB_SEARCH("w"), } /** @@ -291,7 +291,7 @@ public class WhereBuilder { public fun Column.textSearch( query: String, config: String? = null, - type: TextSearchType = TextSearchType.Plain, + type: TextSearchType = TextSearchType.PLAIN, ) { val configPart = if (config != null) "($config)" else "" add(name, "${type.postgrestName}fts$configPart.${encodeValue(query)}") diff --git a/supabase-core/src/commonMain/kotlin/io/github/androidpoet/supabase/core/result/SupabaseError.kt b/supabase-core/src/commonMain/kotlin/io/github/androidpoet/supabase/core/result/SupabaseError.kt index 2e553ab..20bd68d 100644 --- a/supabase-core/src/commonMain/kotlin/io/github/androidpoet/supabase/core/result/SupabaseError.kt +++ b/supabase-core/src/commonMain/kotlin/io/github/androidpoet/supabase/core/result/SupabaseError.kt @@ -65,32 +65,32 @@ public fun SupabaseError.toException(cause: Throwable? = null): SupabaseExceptio */ public enum class SupabaseErrorCategory { /** A uniqueness/foreign-key clash or an already-existing resource (HTTP 409). */ - Conflict, + CONFLICT, /** The requested table, row, user or object does not exist (HTTP 404). */ - NotFound, + NOT_FOUND, /** Missing, invalid or insufficient credentials/permissions (HTTP 401/403). */ - Unauthorized, + UNAUTHORIZED, /** The caller exceeded a rate limit; back off and retry (HTTP 429). */ - RateLimited, + RATE_LIMITED, /** The request was malformed or failed validation (HTTP 400/422). */ - Validation, + VALIDATION, /** A server-side failure unrelated to the request's content (HTTP 5xx). */ - Internal, + INTERNAL, /** * The request never reached the server or never produced a usable response: * the device is offline, the connection timed out, or the body could not be - * decoded. Distinct from [Unknown] so callers can show offline/retry UI. + * decoded. Distinct from [UNKNOWN] so callers can show offline/retry UI. */ - Network, + NETWORK, /** No more specific category applied; the catch-all fallback. */ - Unknown, + UNKNOWN, } /** @@ -101,9 +101,9 @@ public enum class SupabaseErrorCategory { public val SupabaseErrorCategory.isRetryable: Boolean get() = when (this) { - SupabaseErrorCategory.RateLimited, - SupabaseErrorCategory.Internal, - SupabaseErrorCategory.Network, + SupabaseErrorCategory.RATE_LIMITED, + SupabaseErrorCategory.INTERNAL, + SupabaseErrorCategory.NETWORK, -> true else -> false } @@ -198,13 +198,13 @@ private val internalCodes = public val SupabaseError.category: SupabaseErrorCategory get() = when { - code in networkCodes -> SupabaseErrorCategory.Network - code in conflictCodes -> SupabaseErrorCategory.Conflict - code in notFoundCodes -> SupabaseErrorCategory.NotFound - code in unauthorizedCodes -> SupabaseErrorCategory.Unauthorized - code in rateLimitedCodes -> SupabaseErrorCategory.RateLimited - code in validationCodes -> SupabaseErrorCategory.Validation - code in internalCodes -> SupabaseErrorCategory.Internal + code in networkCodes -> SupabaseErrorCategory.NETWORK + code in conflictCodes -> SupabaseErrorCategory.CONFLICT + code in notFoundCodes -> SupabaseErrorCategory.NOT_FOUND + code in unauthorizedCodes -> SupabaseErrorCategory.UNAUTHORIZED + code in rateLimitedCodes -> SupabaseErrorCategory.RATE_LIMITED + code in validationCodes -> SupabaseErrorCategory.VALIDATION + code in internalCodes -> SupabaseErrorCategory.INTERNAL // Fall back to httpStatus; only treat a numeric textual `code` as a // status when it's in the HTTP range, so a numeric service code (e.g. a // 5-digit SQLSTATE) is never misread as an HTTP status. @@ -217,19 +217,19 @@ public val SupabaseError.category: SupabaseErrorCategory // field are still categorized instead of collapsing to Unknown. private fun categorizeByStatus(status: Int?): SupabaseErrorCategory = when (status) { - 401, 403 -> SupabaseErrorCategory.Unauthorized - 404 -> SupabaseErrorCategory.NotFound - 409 -> SupabaseErrorCategory.Conflict + 401, 403 -> SupabaseErrorCategory.UNAUTHORIZED + 404 -> SupabaseErrorCategory.NOT_FOUND + 409 -> SupabaseErrorCategory.CONFLICT // 406 Not Acceptable (Accept/cardinality unsatisfiable) and 416 Range Not // Satisfiable are request-shape problems, so they map to Validation too. - 400, 406, 416, 422 -> SupabaseErrorCategory.Validation - 429 -> SupabaseErrorCategory.RateLimited + 400, 406, 416, 422 -> SupabaseErrorCategory.VALIDATION + 429 -> SupabaseErrorCategory.RATE_LIMITED // 408 Request Timeout and 425 Too Early are transient: treat them like a // server-side hiccup (Internal) so they're retryable and the session // transient-failure guard doesn't sign the user out on a fluke timeout. - 408, 425 -> SupabaseErrorCategory.Internal - in 500..599 -> SupabaseErrorCategory.Internal - else -> SupabaseErrorCategory.Unknown + 408, 425 -> SupabaseErrorCategory.INTERNAL + in 500..599 -> SupabaseErrorCategory.INTERNAL + else -> SupabaseErrorCategory.UNKNOWN } /** True when this is a Postgres unique-constraint violation (`23505`) — a duplicate row. */ @@ -255,7 +255,7 @@ public fun SupabaseError.isFileNotFound(): Boolean = /** * True when the request failed before producing a usable server response * (offline, timeout, connection or decoding failure). Equivalent to - * `category == SupabaseErrorCategory.Network`. + * `category == SupabaseErrorCategory.NETWORK`. */ public fun SupabaseError.isNetworkError(): Boolean = - category == SupabaseErrorCategory.Network + category == SupabaseErrorCategory.NETWORK diff --git a/supabase-core/src/commonMain/kotlin/io/github/androidpoet/supabase/core/result/SupabaseErrorCodes.kt b/supabase-core/src/commonMain/kotlin/io/github/androidpoet/supabase/core/result/SupabaseErrorCodes.kt index 86f08f6..539c0be 100644 --- a/supabase-core/src/commonMain/kotlin/io/github/androidpoet/supabase/core/result/SupabaseErrorCodes.kt +++ b/supabase-core/src/commonMain/kotlin/io/github/androidpoet/supabase/core/result/SupabaseErrorCodes.kt @@ -147,8 +147,8 @@ public object SupabaseErrorCodes { * Client-side (non-HTTP) error codes synthesized by the SDK when a request * never produced a server response — e.g. the device is offline, the * connection timed out, or a response body could not be parsed. These let - * [SupabaseError.category] resolve to [SupabaseErrorCategory.Network] instead - * of the catch-all [SupabaseErrorCategory.Unknown]. + * [SupabaseError.category] resolve to [SupabaseErrorCategory.NETWORK] instead + * of the catch-all [SupabaseErrorCategory.UNKNOWN]. */ public object Client { public const val NETWORK_ERROR: String = "network_error" diff --git a/supabase-core/src/commonMain/kotlin/io/github/androidpoet/supabase/core/result/SupabaseResult.kt b/supabase-core/src/commonMain/kotlin/io/github/androidpoet/supabase/core/result/SupabaseResult.kt index 38d0993..bad9da8 100644 --- a/supabase-core/src/commonMain/kotlin/io/github/androidpoet/supabase/core/result/SupabaseResult.kt +++ b/supabase-core/src/commonMain/kotlin/io/github/androidpoet/supabase/core/result/SupabaseResult.kt @@ -236,23 +236,23 @@ public inline fun SupabaseResult.onFailureCategory( public inline fun SupabaseResult.onConflict( action: (SupabaseError) -> Unit, -): SupabaseResult = onFailureCategory(SupabaseErrorCategory.Conflict, action) +): SupabaseResult = onFailureCategory(SupabaseErrorCategory.CONFLICT, action) public inline fun SupabaseResult.onNotFound( action: (SupabaseError) -> Unit, -): SupabaseResult = onFailureCategory(SupabaseErrorCategory.NotFound, action) +): SupabaseResult = onFailureCategory(SupabaseErrorCategory.NOT_FOUND, action) public inline fun SupabaseResult.onUnauthorized( action: (SupabaseError) -> Unit, -): SupabaseResult = onFailureCategory(SupabaseErrorCategory.Unauthorized, action) +): SupabaseResult = onFailureCategory(SupabaseErrorCategory.UNAUTHORIZED, action) public inline fun SupabaseResult.onRateLimited( action: (SupabaseError) -> Unit, -): SupabaseResult = onFailureCategory(SupabaseErrorCategory.RateLimited, action) +): SupabaseResult = onFailureCategory(SupabaseErrorCategory.RATE_LIMITED, action) public inline fun SupabaseResult.onNetworkError( action: (SupabaseError) -> Unit, -): SupabaseResult = onFailureCategory(SupabaseErrorCategory.Network, action) +): SupabaseResult = onFailureCategory(SupabaseErrorCategory.NETWORK, action) public inline fun SupabaseResult.mapError( transform: (SupabaseError) -> SupabaseError, diff --git a/supabase-core/src/commonTest/kotlin/io/github/androidpoet/supabase/core/SupabaseErrorCategoryTest.kt b/supabase-core/src/commonTest/kotlin/io/github/androidpoet/supabase/core/SupabaseErrorCategoryTest.kt index ca19ab2..21bceb9 100644 --- a/supabase-core/src/commonTest/kotlin/io/github/androidpoet/supabase/core/SupabaseErrorCategoryTest.kt +++ b/supabase-core/src/commonTest/kotlin/io/github/androidpoet/supabase/core/SupabaseErrorCategoryTest.kt @@ -20,42 +20,42 @@ class SupabaseErrorCategoryTest { code = SupabaseErrorCodes.Database.UNIQUENESS_VIOLATION, ) - assertEquals(SupabaseErrorCategory.Conflict, error.category) + assertEquals(SupabaseErrorCategory.CONFLICT, error.category) } @Test fun test_category_mapsHttpStatusUnauthorized() { val error = SupabaseError(message = "unauthorized", code = "401") - assertEquals(SupabaseErrorCategory.Unauthorized, error.category) + assertEquals(SupabaseErrorCategory.UNAUTHORIZED, error.category) } @Test fun test_category_mapsHttpStatusConflict() { val error = SupabaseError(message = "conflict", code = "409") - assertEquals(SupabaseErrorCategory.Conflict, error.category) + assertEquals(SupabaseErrorCategory.CONFLICT, error.category) } @Test fun test_category_mapsHttpStatusRateLimited() { val error = SupabaseError(message = "rate limit", code = "429") - assertEquals(SupabaseErrorCategory.RateLimited, error.category) + assertEquals(SupabaseErrorCategory.RATE_LIMITED, error.category) } @Test fun test_category_mapsHttpStatusValidation() { val error = SupabaseError(message = "validation", code = "422") - assertEquals(SupabaseErrorCategory.Validation, error.category) + assertEquals(SupabaseErrorCategory.VALIDATION, error.category) } @Test fun test_category_unknownForUnrecognizedCode() { val error = SupabaseError(message = "unknown", code = "XYZ") - assertEquals(SupabaseErrorCategory.Unknown, error.category) + assertEquals(SupabaseErrorCategory.UNKNOWN, error.category) } @Test @@ -64,32 +64,32 @@ class SupabaseErrorCategoryTest { // but do carry an HTTP status — must categorize, not fall to Unknown. val error = SupabaseError(message = "not found", code = null, httpStatus = 404) - assertEquals(SupabaseErrorCategory.NotFound, error.category) + assertEquals(SupabaseErrorCategory.NOT_FOUND, error.category) } @Test fun test_category_mapsHttpStatus400ToValidation() { val error = SupabaseError(message = "bad request", httpStatus = 400) - assertEquals(SupabaseErrorCategory.Validation, error.category) + assertEquals(SupabaseErrorCategory.VALIDATION, error.category) } @Test fun test_category_networkCodeIsNetworkAndRetryable() { val error = SupabaseError(message = "offline", code = SupabaseErrorCodes.Client.NETWORK_ERROR) - assertEquals(SupabaseErrorCategory.Network, error.category) + assertEquals(SupabaseErrorCategory.NETWORK, error.category) assertTrue(error.isNetworkError()) assertTrue(error.category.isRetryable) } @Test fun test_isRetryable_byCategory() { - assertTrue(SupabaseErrorCategory.RateLimited.isRetryable) - assertTrue(SupabaseErrorCategory.Internal.isRetryable) - assertTrue(SupabaseErrorCategory.Network.isRetryable) - assertFalse(SupabaseErrorCategory.Validation.isRetryable) - assertFalse(SupabaseErrorCategory.NotFound.isRetryable) - assertFalse(SupabaseErrorCategory.Unauthorized.isRetryable) + assertTrue(SupabaseErrorCategory.RATE_LIMITED.isRetryable) + assertTrue(SupabaseErrorCategory.INTERNAL.isRetryable) + assertTrue(SupabaseErrorCategory.NETWORK.isRetryable) + assertFalse(SupabaseErrorCategory.VALIDATION.isRetryable) + assertFalse(SupabaseErrorCategory.NOT_FOUND.isRetryable) + assertFalse(SupabaseErrorCategory.UNAUTHORIZED.isRetryable) } } diff --git a/supabase-core/src/commonTest/kotlin/io/github/androidpoet/supabase/core/SupabaseResultTest.kt b/supabase-core/src/commonTest/kotlin/io/github/androidpoet/supabase/core/SupabaseResultTest.kt index c157132..e7b8357 100644 --- a/supabase-core/src/commonTest/kotlin/io/github/androidpoet/supabase/core/SupabaseResultTest.kt +++ b/supabase-core/src/commonTest/kotlin/io/github/androidpoet/supabase/core/SupabaseResultTest.kt @@ -385,7 +385,7 @@ class SupabaseResultTest { val notFoundError = SupabaseError(message = "table not found", code = "PGRST205") var called = false SupabaseResult.Failure(notFoundError).onFailureCategorySuspend( - category = io.github.androidpoet.supabase.core.result.SupabaseErrorCategory.NotFound, + category = io.github.androidpoet.supabase.core.result.SupabaseErrorCategory.NOT_FOUND, ) { called = true } assertTrue(called) } @@ -406,7 +406,7 @@ class SupabaseResultTest { runTest { var called = false SupabaseResult.Failure(error).onFailureCategorySuspend( - category = io.github.androidpoet.supabase.core.result.SupabaseErrorCategory.Conflict, + category = io.github.androidpoet.supabase.core.result.SupabaseErrorCategory.CONFLICT, ) { called = true } assertFalse(called) } diff --git a/supabase-database/src/commonMain/kotlin/io/github/androidpoet/supabase/database/DatabaseClientExt.kt b/supabase-database/src/commonMain/kotlin/io/github/androidpoet/supabase/database/DatabaseClientExt.kt index 0262647..ff3cbab 100644 --- a/supabase-database/src/commonMain/kotlin/io/github/androidpoet/supabase/database/DatabaseClientExt.kt +++ b/supabase-database/src/commonMain/kotlin/io/github/androidpoet/supabase/database/DatabaseClientExt.kt @@ -26,7 +26,7 @@ import kotlinx.serialization.json.JsonPrimitive internal fun SupabaseError.isSingleObjectNoRows(): Boolean { val singular = httpStatus == 406 || code == SupabaseErrorCodes.Database.SINGULAR_RESPONSE_VIOLATION val zeroRows = (details as? JsonPrimitive)?.content?.contains("0 rows") == true - return (singular && zeroRows) || category == SupabaseErrorCategory.NotFound + return (singular && zeroRows) || category == SupabaseErrorCategory.NOT_FOUND } /** @@ -114,7 +114,7 @@ public suspend fun DatabaseClient.selectGeoJson( * Reads the single row matching [filters] and decodes it into [T]. * * Requests `single = true`, so PostgREST returns 406 (a [SupabaseResult.Failure] - * with [SupabaseErrorCategory.NotFound]) when zero or more than one row matches — + * with [SupabaseErrorCategory.NOT_FOUND]) when zero or more than one row matches — * use [selectMaybeSingleTyped] if "no row" should be a success with `null`. */ public suspend inline fun DatabaseClient.selectSingleTyped( @@ -129,7 +129,7 @@ public suspend inline fun DatabaseClient.selectSingleTyped( * Reads at most one row matching [filters], returning `null` instead of failing * when none exists — the lenient variant of [selectSingleTyped]. * - * On a "no rows" response (HTTP 406 / [SupabaseErrorCategory.NotFound]) this maps + * On a "no rows" response (HTTP 406 / [SupabaseErrorCategory.NOT_FOUND]) this maps * to `SupabaseResult.Success(null)`; any other failure is propagated unchanged. * Still fails if more than one row matches. */ @@ -561,7 +561,7 @@ public suspend inline fun DatabaseClie * decoded into [T]. * * Requests `single = true`, so a result of zero or more than one row fails with - * HTTP 406 / [SupabaseErrorCategory.NotFound]; use [rpcMaybeSingleTyped] to treat + * HTTP 406 / [SupabaseErrorCategory.NOT_FOUND]; use [rpcMaybeSingleTyped] to treat * "no row" as `null`. */ public suspend inline fun DatabaseClient.rpcSingleTyped( @@ -593,7 +593,7 @@ public suspend inline fun DatabaseClie * `null` instead of failing when none is produced — the lenient variant of * [rpcSingleTyped]. * - * A "no rows" response (HTTP 406 / [SupabaseErrorCategory.NotFound]) maps to + * A "no rows" response (HTTP 406 / [SupabaseErrorCategory.NOT_FOUND]) maps to * `SupabaseResult.Success(null)`; other failures propagate unchanged. */ public suspend inline fun DatabaseClient.rpcMaybeSingleTyped( @@ -817,7 +817,7 @@ public suspend inline fun DatabaseClie /** * Calls read-only stored procedure [function] (GET) expecting exactly one * row/scalar, decoded into [T] — the GET counterpart of [rpcSingleTyped]. Fails - * with HTTP 406 / [SupabaseErrorCategory.NotFound] when not exactly one row is + * with HTTP 406 / [SupabaseErrorCategory.NOT_FOUND] when not exactly one row is * returned. */ public suspend inline fun DatabaseClient.rpcGetSingleTyped( @@ -841,7 +841,7 @@ public suspend inline fun DatabaseClie /** * Calls read-only stored procedure [function] (GET) expecting at most one row, * returning `null` instead of failing when none is produced — the GET, lenient - * counterpart of [rpcSingleTyped] (HTTP 406 / [SupabaseErrorCategory.NotFound] + * counterpart of [rpcSingleTyped] (HTTP 406 / [SupabaseErrorCategory.NOT_FOUND] * maps to `null`). */ public suspend inline fun DatabaseClient.rpcGetMaybeSingleTyped( diff --git a/supabase-realtime/api/android/supabase-realtime.api b/supabase-realtime/api/android/supabase-realtime.api index f54ffb8..eb0f177 100644 --- a/supabase-realtime/api/android/supabase-realtime.api +++ b/supabase-realtime/api/android/supabase-realtime.api @@ -440,7 +440,7 @@ public abstract interface class io/github/androidpoet/supabase/realtime/Realtime public abstract fun broadcastBinary (Ljava/lang/String;[BLkotlin/coroutines/Continuation;)Ljava/lang/Object; public abstract fun broadcastWithAck (Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;JLkotlin/coroutines/Continuation;)Ljava/lang/Object; public static synthetic fun broadcastWithAck$default (Lio/github/androidpoet/supabase/realtime/RealtimeSubscription;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;JLkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; - public abstract fun getChannel ()Ljava/lang/String; + public abstract fun getChannelName ()Ljava/lang/String; public abstract fun getStatus ()Lkotlinx/coroutines/flow/StateFlow; public abstract fun presenceState ()Ljava/util/Map; public abstract fun send (Lio/github/androidpoet/supabase/realtime/RealtimeSubscription$SendType;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; diff --git a/supabase-realtime/api/jvm/supabase-realtime.api b/supabase-realtime/api/jvm/supabase-realtime.api index f54ffb8..eb0f177 100644 --- a/supabase-realtime/api/jvm/supabase-realtime.api +++ b/supabase-realtime/api/jvm/supabase-realtime.api @@ -440,7 +440,7 @@ public abstract interface class io/github/androidpoet/supabase/realtime/Realtime public abstract fun broadcastBinary (Ljava/lang/String;[BLkotlin/coroutines/Continuation;)Ljava/lang/Object; public abstract fun broadcastWithAck (Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;JLkotlin/coroutines/Continuation;)Ljava/lang/Object; public static synthetic fun broadcastWithAck$default (Lio/github/androidpoet/supabase/realtime/RealtimeSubscription;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;JLkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; - public abstract fun getChannel ()Ljava/lang/String; + public abstract fun getChannelName ()Ljava/lang/String; public abstract fun getStatus ()Lkotlinx/coroutines/flow/StateFlow; public abstract fun presenceState ()Ljava/util/Map; public abstract fun send (Lio/github/androidpoet/supabase/realtime/RealtimeSubscription$SendType;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; diff --git a/supabase-realtime/api/supabase-realtime.klib.api b/supabase-realtime/api/supabase-realtime.klib.api index 9a0a1c3..692cd16 100644 --- a/supabase-realtime/api/supabase-realtime.klib.api +++ b/supabase-realtime/api/supabase-realtime.klib.api @@ -63,8 +63,8 @@ abstract interface io.github.androidpoet.supabase.realtime/RealtimeClient { // i } abstract interface io.github.androidpoet.supabase.realtime/RealtimeSubscription { // io.github.androidpoet.supabase.realtime/RealtimeSubscription|null[0] - abstract val channel // io.github.androidpoet.supabase.realtime/RealtimeSubscription.channel|{}channel[0] - abstract fun (): kotlin/String // io.github.androidpoet.supabase.realtime/RealtimeSubscription.channel.|(){}[0] + abstract val channelName // io.github.androidpoet.supabase.realtime/RealtimeSubscription.channelName|{}channelName[0] + abstract fun (): kotlin/String // io.github.androidpoet.supabase.realtime/RealtimeSubscription.channelName.|(){}[0] abstract val status // io.github.androidpoet.supabase.realtime/RealtimeSubscription.status|{}status[0] abstract fun (): kotlinx.coroutines.flow/StateFlow // io.github.androidpoet.supabase.realtime/RealtimeSubscription.status.|(){}[0] diff --git a/supabase-realtime/src/commonMain/kotlin/io/github/androidpoet/supabase/realtime/RealtimeClient.kt b/supabase-realtime/src/commonMain/kotlin/io/github/androidpoet/supabase/realtime/RealtimeClient.kt index dc33a1e..caf5e54 100644 --- a/supabase-realtime/src/commonMain/kotlin/io/github/androidpoet/supabase/realtime/RealtimeClient.kt +++ b/supabase-realtime/src/commonMain/kotlin/io/github/androidpoet/supabase/realtime/RealtimeClient.kt @@ -11,7 +11,7 @@ import kotlinx.serialization.json.JsonObject * multiplexing many Phoenix channels, each carrying `postgres_changes`, * `broadcast` and/or `presence`. * - * Build a channel with [channel] (returning a [RealtimeChannelBuilder] you + * Build a channel with [channelName] (returning a [RealtimeChannelBuilder] you * configure then `subscribe()`), and the socket connects lazily on the first * subscription. The connection auto-reconnects per [RealtimeConfig] and replays * its joins; observe its lifecycle through [connectionState]. Subscriptions are @@ -113,12 +113,12 @@ public interface RealtimeClient { * Sends a broadcast message over HTTP to the realtime broadcast endpoint * (`/realtime/v1/api/broadcast`) without joining a channel or opening a * WebSocket. Useful for fire-and-forget server-to-client fan-out where the - * sender doesn't need to subscribe. [channel] is the channel name (without + * sender doesn't need to subscribe. [channelName] is the channel name (without * the `realtime:` prefix). For a private channel, the caller's session JWT * (or apikey) is attached automatically. */ public suspend fun broadcast( - channel: String, + channelName: String, event: String, payload: JsonObject, private: Boolean = false, diff --git a/supabase-realtime/src/commonMain/kotlin/io/github/androidpoet/supabase/realtime/RealtimeClientExt.kt b/supabase-realtime/src/commonMain/kotlin/io/github/androidpoet/supabase/realtime/RealtimeClientExt.kt index f06dc5a..5a2d12b 100644 --- a/supabase-realtime/src/commonMain/kotlin/io/github/androidpoet/supabase/realtime/RealtimeClientExt.kt +++ b/supabase-realtime/src/commonMain/kotlin/io/github/androidpoet/supabase/realtime/RealtimeClientExt.kt @@ -7,32 +7,32 @@ import kotlinx.coroutines.flow.first import kotlinx.serialization.json.JsonObject /** - * One-call channel subscribe: builds the channel [channel], applies [configure] + * One-call channel subscribe: builds the channel [channelName], applies [configure] * (where you register `onPostgresChange`/`onBroadcast`/`onPresence` and channel * options), and joins it. The lambda form of [RealtimeClient.channel] + * [RealtimeChannelBuilder.subscribe]; returns as soon as the join is sent. */ public suspend fun RealtimeClient.subscribe( - channel: String, + channelName: String, configure: RealtimeChannelBuilder.() -> Unit = {}, ): RealtimeSubscription = - this.channel(channel).apply(configure).subscribe() + this.channel(channelName).apply(configure).subscribe() /** - * Subscribes to [channel] and registers a single raw `postgres_changes` callback + * Subscribes to [channelName] and registers a single raw `postgres_changes` callback * in one call — the shortcut for the common "watch this table" case. See * [RealtimeChannelBuilder.onPostgresChange] for the parameters and use * [realtimeFilter] to build [filter]. */ public suspend fun RealtimeClient.subscribeToPostgresChanges( - channel: String, + channelName: String, schema: String = "public", table: String? = null, filter: String? = null, event: PostgresChangeEvent = PostgresChangeEvent.ALL, callback: suspend (JsonObject) -> Unit, ): RealtimeSubscription = - subscribe(channel) { + subscribe(channelName) { onPostgresChange( schema = schema, table = table, @@ -48,14 +48,14 @@ public suspend fun RealtimeClient.subscribeToPostgresChanges( * INSERT/UPDATE/DELETE. */ public suspend fun RealtimeClient.subscribeToPostgresChanges( - channel: String, + channelName: String, schema: String = "public", table: String? = null, filter: String? = null, event: PostgresChangeEvent = PostgresChangeEvent.ALL, callback: suspend (PostgresChangeEvent, JsonObject) -> Unit, ): RealtimeSubscription = - subscribe(channel) { + subscribe(channelName) { onPostgresChange( schema = schema, table = table, @@ -66,28 +66,28 @@ public suspend fun RealtimeClient.subscribeToPostgresChanges( } /** - * Subscribes to [channel] and registers a single broadcast [callback] for the + * Subscribes to [channelName] and registers a single broadcast [callback] for the * named [event] in one call. See [RealtimeChannelBuilder.onBroadcast]. */ public suspend fun RealtimeClient.subscribeToBroadcast( - channel: String, + channelName: String, event: String, callback: suspend (JsonObject) -> Unit, ): RealtimeSubscription = - subscribe(channel) { + subscribe(channelName) { onBroadcast(event = event, callback = callback) } /** - * Subscribes to [channel] and registers a single presence sync [callback] in one + * Subscribes to [channelName] and registers a single presence sync [callback] in one * call, invoked with the full [PresenceState]. See * [RealtimeChannelBuilder.onPresence]. */ public suspend fun RealtimeClient.subscribeToPresence( - channel: String, + channelName: String, callback: suspend (PresenceState) -> Unit, ): RealtimeSubscription = - subscribe(channel) { + subscribe(channelName) { onPresence(callback = callback) } diff --git a/supabase-realtime/src/commonMain/kotlin/io/github/androidpoet/supabase/realtime/RealtimeClientImpl.kt b/supabase-realtime/src/commonMain/kotlin/io/github/androidpoet/supabase/realtime/RealtimeClientImpl.kt index 3fd77bf..d5467b4 100644 --- a/supabase-realtime/src/commonMain/kotlin/io/github/androidpoet/supabase/realtime/RealtimeClientImpl.kt +++ b/supabase-realtime/src/commonMain/kotlin/io/github/androidpoet/supabase/realtime/RealtimeClientImpl.kt @@ -171,11 +171,11 @@ internal class RealtimeClientImpl( synchronized(subscriptionsLock) { activeSubscriptions.values.toSet() } override fun getActiveChannelNames(): Set = - synchronized(subscriptionsLock) { activeSubscriptions.values.mapTo(mutableSetOf()) { it.channel } } + synchronized(subscriptionsLock) { activeSubscriptions.values.mapTo(mutableSetOf()) { it.channelName } } override fun getActiveChannels(): Set = synchronized(subscriptionsLock) { - activeSubscriptions.values.mapTo(mutableSetOf()) { RealtimeChannel(name = it.channel, topic = it.topic) } + activeSubscriptions.values.mapTo(mutableSetOf()) { RealtimeChannel(name = it.channelName, topic = it.topic) } } override suspend fun removeSubscription(subscription: RealtimeSubscription) { @@ -272,7 +272,7 @@ internal class RealtimeClientImpl( } override suspend fun broadcast( - channel: String, + channelName: String, event: String, payload: JsonObject, private: Boolean, @@ -284,7 +284,7 @@ internal class RealtimeClientImpl( JsonArray( listOf( buildJsonObject { - put("topic", JsonPrimitive(channel)) + put("topic", JsonPrimitive(channelName)) put("event", JsonPrimitive(event)) put("payload", payload) put("private", JsonPrimitive(private)) @@ -341,7 +341,7 @@ internal class RealtimeClientImpl( val topic = "realtime:${builder.channelName}" val subscription = ChannelSubscriptionImpl( - channel = builder.channelName, + channelName = builder.channelName, topic = topic, client = this, postgresCallbacks = builder.postgresCallbacks.toList(), @@ -869,7 +869,7 @@ internal class RealtimeClientImpl( } internal class ChannelSubscriptionImpl( - override val channel: String, + override val channelName: String, internal val topic: String, private val client: RealtimeClientImpl, internal val postgresCallbacks: List, @@ -1008,7 +1008,7 @@ internal class ChannelSubscriptionImpl( override suspend fun unsubscribe() { _status.value = RealtimeSubscription.Status.UNSUBSCRIBING - failPendingAcks("Channel '$channel' left before broadcast was acknowledged") + failPendingAcks("Channel '$channelName' left before broadcast was acknowledged") client.leaveChannel(topic) _status.value = RealtimeSubscription.Status.UNSUBSCRIBED } @@ -1063,7 +1063,7 @@ internal class ChannelSubscriptionImpl( ): SupabaseResult { if (_status.value != RealtimeSubscription.Status.SUBSCRIBED) { return SupabaseResult.Failure( - SupabaseError(message = "Channel '$channel' is not subscribed; cannot await broadcast ack"), + SupabaseError(message = "Channel '$channelName' is not subscribed; cannot await broadcast ack"), ) } val ref = client.nextRef() @@ -1129,7 +1129,7 @@ internal class ChannelSubscriptionImpl( "phx_reply" -> handleSystemReply(message.payload, message.ref) "phx_error" -> { _status.value = RealtimeSubscription.Status.ERROR - failPendingAcks("Channel '$channel' errored before broadcast was acknowledged") + failPendingAcks("Channel '$channelName' errored before broadcast was acknowledged") val event = RealtimeEvent.SystemEvent( status = "error", @@ -1153,7 +1153,7 @@ internal class ChannelSubscriptionImpl( // path is left untouched. private suspend fun handleChannelClose(payload: JsonObject) { _status.value = RealtimeSubscription.Status.UNSUBSCRIBED - failPendingAcks("Channel '$channel' closed before broadcast was acknowledged") + failPendingAcks("Channel '$channelName' closed before broadcast was acknowledged") publish( RealtimeEvent.SystemEvent( status = "closed", @@ -1173,7 +1173,7 @@ internal class ChannelSubscriptionImpl( val isError = status == "error" || systemMessage?.contains("Token has expired") == true if (isError) { _status.value = RealtimeSubscription.Status.ERROR - failPendingAcks("Channel '$channel' errored before broadcast was acknowledged") + failPendingAcks("Channel '$channelName' errored before broadcast was acknowledged") } publish( RealtimeEvent.SystemEvent( diff --git a/supabase-realtime/src/commonMain/kotlin/io/github/androidpoet/supabase/realtime/RealtimeSubscription.kt b/supabase-realtime/src/commonMain/kotlin/io/github/androidpoet/supabase/realtime/RealtimeSubscription.kt index fedb2dd..379e546 100644 --- a/supabase-realtime/src/commonMain/kotlin/io/github/androidpoet/supabase/realtime/RealtimeSubscription.kt +++ b/supabase-realtime/src/commonMain/kotlin/io/github/androidpoet/supabase/realtime/RealtimeSubscription.kt @@ -36,7 +36,7 @@ public interface RealtimeSubscription { } /** The channel name (the part after `realtime:`). */ - public val channel: String + public val channelName: String /** The channel's join lifecycle as a [StateFlow]; await `SUBSCRIBED` or use * `awaitSubscribed`. */ diff --git a/supabase-realtime/src/commonMain/kotlin/io/github/androidpoet/supabase/realtime/RealtimeTyped.kt b/supabase-realtime/src/commonMain/kotlin/io/github/androidpoet/supabase/realtime/RealtimeTyped.kt index 730a16c..e5ca657 100644 --- a/supabase-realtime/src/commonMain/kotlin/io/github/androidpoet/supabase/realtime/RealtimeTyped.kt +++ b/supabase-realtime/src/commonMain/kotlin/io/github/androidpoet/supabase/realtime/RealtimeTyped.kt @@ -45,14 +45,14 @@ public inline fun RealtimeChannelBuilder.onPostgresChange( * skip-on-mismatch semantics. */ public suspend inline fun RealtimeClient.subscribeToPostgresChanges( - channel: String, + channelName: String, schema: String = "public", table: String? = null, filter: String? = null, event: PostgresChangeEvent = PostgresChangeEvent.ALL, crossinline onRow: suspend (T) -> Unit, ): RealtimeSubscription = - subscribe(channel) { + subscribe(channelName) { onPostgresChange( schema = schema, table = table, diff --git a/supabase-realtime/src/commonTest/kotlin/io/github/androidpoet/supabase/realtime/RealtimeClientExtTest.kt b/supabase-realtime/src/commonTest/kotlin/io/github/androidpoet/supabase/realtime/RealtimeClientExtTest.kt index 5ec26cd..3a46c06 100644 --- a/supabase-realtime/src/commonTest/kotlin/io/github/androidpoet/supabase/realtime/RealtimeClientExtTest.kt +++ b/supabase-realtime/src/commonTest/kotlin/io/github/androidpoet/supabase/realtime/RealtimeClientExtTest.kt @@ -82,7 +82,7 @@ class RealtimeClientExtTest { configurePresence("user-1") } as ChannelSubscriptionImpl - assertEquals("room-x", subscription.channel) + assertEquals("room-x", subscription.channelName) assertTrue(subscription.privateChannel) assertEquals("user-1", subscription.presenceKey) } @@ -94,7 +94,7 @@ class RealtimeClientExtTest { val subscription = realtime.subscribeToPostgresChanges( - channel = "room-pg", + channelName = "room-pg", schema = "public", table = "messages", event = PostgresChangeEvent.INSERT, @@ -114,7 +114,7 @@ class RealtimeClientExtTest { val subscription = realtime.subscribeToPostgresChanges( - channel = "room-pg-typed", + channelName = "room-pg-typed", table = "messages", event = PostgresChangeEvent.ALL, ) { event, payload -> @@ -156,7 +156,7 @@ class RealtimeClientExtTest { val subscription = realtime.subscribeToBroadcast( - channel = "room-b", + channelName = "room-b", event = "message", callback = { called = true }, ) as ChannelSubscriptionImpl @@ -185,7 +185,7 @@ class RealtimeClientExtTest { val subscription = realtime.subscribeToPresence( - channel = "room-presence", + channelName = "room-presence", callback = { state: PresenceState -> stateSize = state.size }, ) as ChannelSubscriptionImpl @@ -227,7 +227,7 @@ class RealtimeClientExtTest { val subscription = realtime.subscribeToPresence( - channel = "room", + channelName = "room", callback = { state: PresenceState -> latest = state }, ) as ChannelSubscriptionImpl diff --git a/supabase-realtime/src/commonTest/kotlin/io/github/androidpoet/supabase/realtime/RealtimeExtTest.kt b/supabase-realtime/src/commonTest/kotlin/io/github/androidpoet/supabase/realtime/RealtimeExtTest.kt index 989bfd6..4227085 100644 --- a/supabase-realtime/src/commonTest/kotlin/io/github/androidpoet/supabase/realtime/RealtimeExtTest.kt +++ b/supabase-realtime/src/commonTest/kotlin/io/github/androidpoet/supabase/realtime/RealtimeExtTest.kt @@ -97,7 +97,7 @@ private class FakeSubscription : RealtimeSubscription { private val flow = MutableSharedFlow(replay = 8, extraBufferCapacity = 8) private val statusState = MutableStateFlow(RealtimeSubscription.Status.SUBSCRIBING) - override val channel: String = "room" + override val channelName: String = "room" override val status: StateFlow = statusState diff --git a/supabase-storage/api/android/supabase-storage.api b/supabase-storage/api/android/supabase-storage.api index 1891722..ba6e95a 100644 --- a/supabase-storage/api/android/supabase-storage.api +++ b/supabase-storage/api/android/supabase-storage.api @@ -915,16 +915,16 @@ public final class io/github/androidpoet/supabase/storage/models/ObjectListV2Obj public final fun serializer ()Lkotlinx/serialization/KSerializer; } -public final class io/github/androidpoet/supabase/storage/models/ObjectListV2Result { - public static final field Companion Lio/github/androidpoet/supabase/storage/models/ObjectListV2Result$Companion; +public final class io/github/androidpoet/supabase/storage/models/ObjectListV2Response { + public static final field Companion Lio/github/androidpoet/supabase/storage/models/ObjectListV2Response$Companion; public fun (ZLjava/util/List;Ljava/util/List;Ljava/lang/String;)V public synthetic fun (ZLjava/util/List;Ljava/util/List;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun component1 ()Z public final fun component2 ()Ljava/util/List; public final fun component3 ()Ljava/util/List; public final fun component4 ()Ljava/lang/String; - public final fun copy (ZLjava/util/List;Ljava/util/List;Ljava/lang/String;)Lio/github/androidpoet/supabase/storage/models/ObjectListV2Result; - public static synthetic fun copy$default (Lio/github/androidpoet/supabase/storage/models/ObjectListV2Result;ZLjava/util/List;Ljava/util/List;Ljava/lang/String;ILjava/lang/Object;)Lio/github/androidpoet/supabase/storage/models/ObjectListV2Result; + public final fun copy (ZLjava/util/List;Ljava/util/List;Ljava/lang/String;)Lio/github/androidpoet/supabase/storage/models/ObjectListV2Response; + public static synthetic fun copy$default (Lio/github/androidpoet/supabase/storage/models/ObjectListV2Response;ZLjava/util/List;Ljava/util/List;Ljava/lang/String;ILjava/lang/Object;)Lio/github/androidpoet/supabase/storage/models/ObjectListV2Response; public fun equals (Ljava/lang/Object;)Z public final fun getFolders ()Ljava/util/List; public final fun getHasNext ()Z @@ -934,18 +934,18 @@ public final class io/github/androidpoet/supabase/storage/models/ObjectListV2Res public fun toString ()Ljava/lang/String; } -public final synthetic class io/github/androidpoet/supabase/storage/models/ObjectListV2Result$$serializer : kotlinx/serialization/internal/GeneratedSerializer { - public static final field INSTANCE Lio/github/androidpoet/supabase/storage/models/ObjectListV2Result$$serializer; +public final synthetic class io/github/androidpoet/supabase/storage/models/ObjectListV2Response$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lio/github/androidpoet/supabase/storage/models/ObjectListV2Response$$serializer; public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; - public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lio/github/androidpoet/supabase/storage/models/ObjectListV2Result; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lio/github/androidpoet/supabase/storage/models/ObjectListV2Response; public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; - public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lio/github/androidpoet/supabase/storage/models/ObjectListV2Result;)V + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lio/github/androidpoet/supabase/storage/models/ObjectListV2Response;)V public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; } -public final class io/github/androidpoet/supabase/storage/models/ObjectListV2Result$Companion { +public final class io/github/androidpoet/supabase/storage/models/ObjectListV2Response$Companion { public final fun serializer ()Lkotlinx/serialization/KSerializer; } diff --git a/supabase-storage/api/jvm/supabase-storage.api b/supabase-storage/api/jvm/supabase-storage.api index 1891722..ba6e95a 100644 --- a/supabase-storage/api/jvm/supabase-storage.api +++ b/supabase-storage/api/jvm/supabase-storage.api @@ -915,16 +915,16 @@ public final class io/github/androidpoet/supabase/storage/models/ObjectListV2Obj public final fun serializer ()Lkotlinx/serialization/KSerializer; } -public final class io/github/androidpoet/supabase/storage/models/ObjectListV2Result { - public static final field Companion Lio/github/androidpoet/supabase/storage/models/ObjectListV2Result$Companion; +public final class io/github/androidpoet/supabase/storage/models/ObjectListV2Response { + public static final field Companion Lio/github/androidpoet/supabase/storage/models/ObjectListV2Response$Companion; public fun (ZLjava/util/List;Ljava/util/List;Ljava/lang/String;)V public synthetic fun (ZLjava/util/List;Ljava/util/List;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun component1 ()Z public final fun component2 ()Ljava/util/List; public final fun component3 ()Ljava/util/List; public final fun component4 ()Ljava/lang/String; - public final fun copy (ZLjava/util/List;Ljava/util/List;Ljava/lang/String;)Lio/github/androidpoet/supabase/storage/models/ObjectListV2Result; - public static synthetic fun copy$default (Lio/github/androidpoet/supabase/storage/models/ObjectListV2Result;ZLjava/util/List;Ljava/util/List;Ljava/lang/String;ILjava/lang/Object;)Lio/github/androidpoet/supabase/storage/models/ObjectListV2Result; + public final fun copy (ZLjava/util/List;Ljava/util/List;Ljava/lang/String;)Lio/github/androidpoet/supabase/storage/models/ObjectListV2Response; + public static synthetic fun copy$default (Lio/github/androidpoet/supabase/storage/models/ObjectListV2Response;ZLjava/util/List;Ljava/util/List;Ljava/lang/String;ILjava/lang/Object;)Lio/github/androidpoet/supabase/storage/models/ObjectListV2Response; public fun equals (Ljava/lang/Object;)Z public final fun getFolders ()Ljava/util/List; public final fun getHasNext ()Z @@ -934,18 +934,18 @@ public final class io/github/androidpoet/supabase/storage/models/ObjectListV2Res public fun toString ()Ljava/lang/String; } -public final synthetic class io/github/androidpoet/supabase/storage/models/ObjectListV2Result$$serializer : kotlinx/serialization/internal/GeneratedSerializer { - public static final field INSTANCE Lio/github/androidpoet/supabase/storage/models/ObjectListV2Result$$serializer; +public final synthetic class io/github/androidpoet/supabase/storage/models/ObjectListV2Response$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lio/github/androidpoet/supabase/storage/models/ObjectListV2Response$$serializer; public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; - public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lio/github/androidpoet/supabase/storage/models/ObjectListV2Result; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lio/github/androidpoet/supabase/storage/models/ObjectListV2Response; public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; - public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lio/github/androidpoet/supabase/storage/models/ObjectListV2Result;)V + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lio/github/androidpoet/supabase/storage/models/ObjectListV2Response;)V public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; } -public final class io/github/androidpoet/supabase/storage/models/ObjectListV2Result$Companion { +public final class io/github/androidpoet/supabase/storage/models/ObjectListV2Response$Companion { public final fun serializer ()Lkotlinx/serialization/KSerializer; } diff --git a/supabase-storage/api/supabase-storage.klib.api b/supabase-storage/api/supabase-storage.klib.api index 03a4f2d..f7306f4 100644 --- a/supabase-storage/api/supabase-storage.klib.api +++ b/supabase-storage/api/supabase-storage.klib.api @@ -149,7 +149,7 @@ abstract interface io.github.androidpoet.supabase.storage/StorageClient { // io. abstract suspend fun list(kotlin/String, kotlin/String = ..., kotlin/Int = ..., kotlin/Int = ..., kotlin/String? = ..., io.github.androidpoet.supabase.storage/SortOrder = ..., kotlin/String? = ...): io.github.androidpoet.supabase.core.result/SupabaseResult> // io.github.androidpoet.supabase.storage/StorageClient.list|list(kotlin.String;kotlin.String;kotlin.Int;kotlin.Int;kotlin.String?;io.github.androidpoet.supabase.storage.SortOrder;kotlin.String?){}[0] abstract suspend fun listAnalyticsBuckets(kotlin/Int? = ..., kotlin/Int? = ..., kotlin/String? = ..., io.github.androidpoet.supabase.storage/SortOrder? = ..., kotlin/String? = ...): io.github.androidpoet.supabase.core.result/SupabaseResult> // io.github.androidpoet.supabase.storage/StorageClient.listAnalyticsBuckets|listAnalyticsBuckets(kotlin.Int?;kotlin.Int?;kotlin.String?;io.github.androidpoet.supabase.storage.SortOrder?;kotlin.String?){}[0] abstract suspend fun listBuckets(kotlin/Int? = ..., kotlin/Int? = ..., kotlin/String? = ..., io.github.androidpoet.supabase.storage/SortOrder? = ..., kotlin/String? = ...): io.github.androidpoet.supabase.core.result/SupabaseResult> // io.github.androidpoet.supabase.storage/StorageClient.listBuckets|listBuckets(kotlin.Int?;kotlin.Int?;kotlin.String?;io.github.androidpoet.supabase.storage.SortOrder?;kotlin.String?){}[0] - abstract suspend fun listV2(kotlin/String, kotlin/String? = ..., kotlin/String? = ..., kotlin/Int? = ..., kotlin/Boolean? = ..., kotlin/String? = ..., io.github.androidpoet.supabase.storage/SortOrder = ...): io.github.androidpoet.supabase.core.result/SupabaseResult // io.github.androidpoet.supabase.storage/StorageClient.listV2|listV2(kotlin.String;kotlin.String?;kotlin.String?;kotlin.Int?;kotlin.Boolean?;kotlin.String?;io.github.androidpoet.supabase.storage.SortOrder){}[0] + abstract suspend fun listV2(kotlin/String, kotlin/String? = ..., kotlin/String? = ..., kotlin/Int? = ..., kotlin/Boolean? = ..., kotlin/String? = ..., io.github.androidpoet.supabase.storage/SortOrder = ...): io.github.androidpoet.supabase.core.result/SupabaseResult // io.github.androidpoet.supabase.storage/StorageClient.listV2|listV2(kotlin.String;kotlin.String?;kotlin.String?;kotlin.Int?;kotlin.Boolean?;kotlin.String?;io.github.androidpoet.supabase.storage.SortOrder){}[0] abstract suspend fun listVectorBuckets(kotlin/String? = ..., kotlin/Int? = ..., kotlin/String? = ...): io.github.androidpoet.supabase.core.result/SupabaseResult // io.github.androidpoet.supabase.storage/StorageClient.listVectorBuckets|listVectorBuckets(kotlin.String?;kotlin.Int?;kotlin.String?){}[0] abstract suspend fun listVectorIndexes(kotlin/String, kotlin/String? = ..., kotlin/Int? = ..., kotlin/String? = ...): io.github.androidpoet.supabase.core.result/SupabaseResult // io.github.androidpoet.supabase.storage/StorageClient.listVectorIndexes|listVectorIndexes(kotlin.String;kotlin.String?;kotlin.Int?;kotlin.String?){}[0] abstract suspend fun listVectors(kotlin/String, kotlin/String, kotlin/Int? = ..., kotlin/String? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Int? = ..., kotlin/Int? = ...): io.github.androidpoet.supabase.core.result/SupabaseResult // io.github.androidpoet.supabase.storage/StorageClient.listVectors|listVectors(kotlin.String;kotlin.String;kotlin.Int?;kotlin.String?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Int?;kotlin.Int?){}[0] @@ -776,40 +776,40 @@ final class io.github.androidpoet.supabase.storage.models/ObjectListV2Object { / } } -final class io.github.androidpoet.supabase.storage.models/ObjectListV2Result { // io.github.androidpoet.supabase.storage.models/ObjectListV2Result|null[0] - constructor (kotlin/Boolean, kotlin.collections/List, kotlin.collections/List, kotlin/String? = ...) // io.github.androidpoet.supabase.storage.models/ObjectListV2Result.|(kotlin.Boolean;kotlin.collections.List;kotlin.collections.List;kotlin.String?){}[0] +final class io.github.androidpoet.supabase.storage.models/ObjectListV2Response { // io.github.androidpoet.supabase.storage.models/ObjectListV2Response|null[0] + constructor (kotlin/Boolean, kotlin.collections/List, kotlin.collections/List, kotlin/String? = ...) // io.github.androidpoet.supabase.storage.models/ObjectListV2Response.|(kotlin.Boolean;kotlin.collections.List;kotlin.collections.List;kotlin.String?){}[0] - final val folders // io.github.androidpoet.supabase.storage.models/ObjectListV2Result.folders|{}folders[0] - final fun (): kotlin.collections/List // io.github.androidpoet.supabase.storage.models/ObjectListV2Result.folders.|(){}[0] - final val hasNext // io.github.androidpoet.supabase.storage.models/ObjectListV2Result.hasNext|{}hasNext[0] - final fun (): kotlin/Boolean // io.github.androidpoet.supabase.storage.models/ObjectListV2Result.hasNext.|(){}[0] - final val nextCursor // io.github.androidpoet.supabase.storage.models/ObjectListV2Result.nextCursor|{}nextCursor[0] - final fun (): kotlin/String? // io.github.androidpoet.supabase.storage.models/ObjectListV2Result.nextCursor.|(){}[0] - final val objects // io.github.androidpoet.supabase.storage.models/ObjectListV2Result.objects|{}objects[0] - final fun (): kotlin.collections/List // io.github.androidpoet.supabase.storage.models/ObjectListV2Result.objects.|(){}[0] + final val folders // io.github.androidpoet.supabase.storage.models/ObjectListV2Response.folders|{}folders[0] + final fun (): kotlin.collections/List // io.github.androidpoet.supabase.storage.models/ObjectListV2Response.folders.|(){}[0] + final val hasNext // io.github.androidpoet.supabase.storage.models/ObjectListV2Response.hasNext|{}hasNext[0] + final fun (): kotlin/Boolean // io.github.androidpoet.supabase.storage.models/ObjectListV2Response.hasNext.|(){}[0] + final val nextCursor // io.github.androidpoet.supabase.storage.models/ObjectListV2Response.nextCursor|{}nextCursor[0] + final fun (): kotlin/String? // io.github.androidpoet.supabase.storage.models/ObjectListV2Response.nextCursor.|(){}[0] + final val objects // io.github.androidpoet.supabase.storage.models/ObjectListV2Response.objects|{}objects[0] + final fun (): kotlin.collections/List // io.github.androidpoet.supabase.storage.models/ObjectListV2Response.objects.|(){}[0] - final fun component1(): kotlin/Boolean // io.github.androidpoet.supabase.storage.models/ObjectListV2Result.component1|component1(){}[0] - final fun component2(): kotlin.collections/List // io.github.androidpoet.supabase.storage.models/ObjectListV2Result.component2|component2(){}[0] - final fun component3(): kotlin.collections/List // io.github.androidpoet.supabase.storage.models/ObjectListV2Result.component3|component3(){}[0] - final fun component4(): kotlin/String? // io.github.androidpoet.supabase.storage.models/ObjectListV2Result.component4|component4(){}[0] - final fun copy(kotlin/Boolean = ..., kotlin.collections/List = ..., kotlin.collections/List = ..., kotlin/String? = ...): io.github.androidpoet.supabase.storage.models/ObjectListV2Result // io.github.androidpoet.supabase.storage.models/ObjectListV2Result.copy|copy(kotlin.Boolean;kotlin.collections.List;kotlin.collections.List;kotlin.String?){}[0] - final fun equals(kotlin/Any?): kotlin/Boolean // io.github.androidpoet.supabase.storage.models/ObjectListV2Result.equals|equals(kotlin.Any?){}[0] - final fun hashCode(): kotlin/Int // io.github.androidpoet.supabase.storage.models/ObjectListV2Result.hashCode|hashCode(){}[0] - final fun toString(): kotlin/String // io.github.androidpoet.supabase.storage.models/ObjectListV2Result.toString|toString(){}[0] + final fun component1(): kotlin/Boolean // io.github.androidpoet.supabase.storage.models/ObjectListV2Response.component1|component1(){}[0] + final fun component2(): kotlin.collections/List // io.github.androidpoet.supabase.storage.models/ObjectListV2Response.component2|component2(){}[0] + final fun component3(): kotlin.collections/List // io.github.androidpoet.supabase.storage.models/ObjectListV2Response.component3|component3(){}[0] + final fun component4(): kotlin/String? // io.github.androidpoet.supabase.storage.models/ObjectListV2Response.component4|component4(){}[0] + final fun copy(kotlin/Boolean = ..., kotlin.collections/List = ..., kotlin.collections/List = ..., kotlin/String? = ...): io.github.androidpoet.supabase.storage.models/ObjectListV2Response // io.github.androidpoet.supabase.storage.models/ObjectListV2Response.copy|copy(kotlin.Boolean;kotlin.collections.List;kotlin.collections.List;kotlin.String?){}[0] + final fun equals(kotlin/Any?): kotlin/Boolean // io.github.androidpoet.supabase.storage.models/ObjectListV2Response.equals|equals(kotlin.Any?){}[0] + final fun hashCode(): kotlin/Int // io.github.androidpoet.supabase.storage.models/ObjectListV2Response.hashCode|hashCode(){}[0] + final fun toString(): kotlin/String // io.github.androidpoet.supabase.storage.models/ObjectListV2Response.toString|toString(){}[0] - final object $serializer : kotlinx.serialization.internal/GeneratedSerializer { // io.github.androidpoet.supabase.storage.models/ObjectListV2Result.$serializer|null[0] - final val descriptor // io.github.androidpoet.supabase.storage.models/ObjectListV2Result.$serializer.descriptor|{}descriptor[0] - final fun (): kotlinx.serialization.descriptors/SerialDescriptor // io.github.androidpoet.supabase.storage.models/ObjectListV2Result.$serializer.descriptor.|(){}[0] + final object $serializer : kotlinx.serialization.internal/GeneratedSerializer { // io.github.androidpoet.supabase.storage.models/ObjectListV2Response.$serializer|null[0] + final val descriptor // io.github.androidpoet.supabase.storage.models/ObjectListV2Response.$serializer.descriptor|{}descriptor[0] + final fun (): kotlinx.serialization.descriptors/SerialDescriptor // io.github.androidpoet.supabase.storage.models/ObjectListV2Response.$serializer.descriptor.|(){}[0] - final fun childSerializers(): kotlin/Array> // io.github.androidpoet.supabase.storage.models/ObjectListV2Result.$serializer.childSerializers|childSerializers(){}[0] - final fun deserialize(kotlinx.serialization.encoding/Decoder): io.github.androidpoet.supabase.storage.models/ObjectListV2Result // io.github.androidpoet.supabase.storage.models/ObjectListV2Result.$serializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0] - final fun serialize(kotlinx.serialization.encoding/Encoder, io.github.androidpoet.supabase.storage.models/ObjectListV2Result) // io.github.androidpoet.supabase.storage.models/ObjectListV2Result.$serializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;io.github.androidpoet.supabase.storage.models.ObjectListV2Result){}[0] + final fun childSerializers(): kotlin/Array> // io.github.androidpoet.supabase.storage.models/ObjectListV2Response.$serializer.childSerializers|childSerializers(){}[0] + final fun deserialize(kotlinx.serialization.encoding/Decoder): io.github.androidpoet.supabase.storage.models/ObjectListV2Response // io.github.androidpoet.supabase.storage.models/ObjectListV2Response.$serializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0] + final fun serialize(kotlinx.serialization.encoding/Encoder, io.github.androidpoet.supabase.storage.models/ObjectListV2Response) // io.github.androidpoet.supabase.storage.models/ObjectListV2Response.$serializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;io.github.androidpoet.supabase.storage.models.ObjectListV2Response){}[0] } - final object Companion { // io.github.androidpoet.supabase.storage.models/ObjectListV2Result.Companion|null[0] - final val $childSerializers // io.github.androidpoet.supabase.storage.models/ObjectListV2Result.Companion.$childSerializers|{}$childSerializers[0] + final object Companion { // io.github.androidpoet.supabase.storage.models/ObjectListV2Response.Companion|null[0] + final val $childSerializers // io.github.androidpoet.supabase.storage.models/ObjectListV2Response.Companion.$childSerializers|{}$childSerializers[0] - final fun serializer(): kotlinx.serialization/KSerializer // io.github.androidpoet.supabase.storage.models/ObjectListV2Result.Companion.serializer|serializer(){}[0] + final fun serializer(): kotlinx.serialization/KSerializer // io.github.androidpoet.supabase.storage.models/ObjectListV2Response.Companion.serializer|serializer(){}[0] } } diff --git a/supabase-storage/src/commonMain/kotlin/io/github/androidpoet/supabase/storage/StorageClient.kt b/supabase-storage/src/commonMain/kotlin/io/github/androidpoet/supabase/storage/StorageClient.kt index 5c89a08..2192989 100644 --- a/supabase-storage/src/commonMain/kotlin/io/github/androidpoet/supabase/storage/StorageClient.kt +++ b/supabase-storage/src/commonMain/kotlin/io/github/androidpoet/supabase/storage/StorageClient.kt @@ -15,7 +15,7 @@ import io.github.androidpoet.supabase.storage.models.IcebergTableMetadataRespons import io.github.androidpoet.supabase.storage.models.IcebergTableRegisterRequest import io.github.androidpoet.supabase.storage.models.IcebergUpdateNamespacePropertiesRequest import io.github.androidpoet.supabase.storage.models.IcebergUpdateNamespacePropertiesResponse -import io.github.androidpoet.supabase.storage.models.ObjectListV2Result +import io.github.androidpoet.supabase.storage.models.ObjectListV2Response import io.github.androidpoet.supabase.storage.models.VectorBucket import io.github.androidpoet.supabase.storage.models.VectorBucketListResponse import io.github.androidpoet.supabase.storage.models.VectorData @@ -513,7 +513,7 @@ public interface StorageClient { ): SupabaseResult> /** - * Cursor-paged object listing (`POST /object/list-v2`) returning an [ObjectListV2Result] that + * Cursor-paged object listing (`POST /object/list-v2`) returning an [ObjectListV2Response] that * separates folders from objects and carries a `nextCursor`/`hasNext` for continuation. * Prefer this over [list] for large buckets or delimiter-style folder navigation. * @@ -533,7 +533,7 @@ public interface StorageClient { withDelimiter: Boolean? = null, sortBy: String? = null, sortOrder: SortOrder = SortOrder.ASC, - ): SupabaseResult + ): SupabaseResult /** * Moves (renames) the object at [fromPath] to [toPath] (`POST /object/move`), optionally diff --git a/supabase-storage/src/commonMain/kotlin/io/github/androidpoet/supabase/storage/StorageClientImpl.kt b/supabase-storage/src/commonMain/kotlin/io/github/androidpoet/supabase/storage/StorageClientImpl.kt index 900f301..95c6c19 100644 --- a/supabase-storage/src/commonMain/kotlin/io/github/androidpoet/supabase/storage/StorageClientImpl.kt +++ b/supabase-storage/src/commonMain/kotlin/io/github/androidpoet/supabase/storage/StorageClientImpl.kt @@ -28,7 +28,7 @@ import io.github.androidpoet.supabase.storage.models.IcebergUpdateNamespacePrope import io.github.androidpoet.supabase.storage.models.MoveRequest import io.github.androidpoet.supabase.storage.models.ObjectListRequest import io.github.androidpoet.supabase.storage.models.ObjectListV2Request -import io.github.androidpoet.supabase.storage.models.ObjectListV2Result +import io.github.androidpoet.supabase.storage.models.ObjectListV2Response import io.github.androidpoet.supabase.storage.models.ObjectSortByRequest import io.github.androidpoet.supabase.storage.models.SignedUrlItemResponse import io.github.androidpoet.supabase.storage.models.SignedUrlRequest @@ -315,7 +315,7 @@ internal class StorageClientImpl( when (val result = info(bucket, path)) { is SupabaseResult.Success -> SupabaseResult.Success(true) is SupabaseResult.Failure -> - if (result.error.category == SupabaseErrorCategory.NotFound) { + if (result.error.category == SupabaseErrorCategory.NOT_FOUND) { SupabaseResult.Success(false) } else { result @@ -326,7 +326,7 @@ internal class StorageClientImpl( when (val result = infoPublic(bucket, path)) { is SupabaseResult.Success -> SupabaseResult.Success(true) is SupabaseResult.Failure -> - if (result.error.category == SupabaseErrorCategory.NotFound) { + if (result.error.category == SupabaseErrorCategory.NOT_FOUND) { SupabaseResult.Success(false) } else { result @@ -366,7 +366,7 @@ internal class StorageClientImpl( withDelimiter: Boolean?, sortBy: String?, sortOrder: SortOrder, - ): SupabaseResult { + ): SupabaseResult { val body = defaultJson.encodeToString( ObjectListV2Request( diff --git a/supabase-storage/src/commonMain/kotlin/io/github/androidpoet/supabase/storage/models/StorageModels.kt b/supabase-storage/src/commonMain/kotlin/io/github/androidpoet/supabase/storage/models/StorageModels.kt index f4e8e30..616c8c9 100644 --- a/supabase-storage/src/commonMain/kotlin/io/github/androidpoet/supabase/storage/models/StorageModels.kt +++ b/supabase-storage/src/commonMain/kotlin/io/github/androidpoet/supabase/storage/models/StorageModels.kt @@ -197,7 +197,7 @@ internal data class ObjectListV2Request( ) /** - * A single object entry in a v2 ([ObjectListV2Result]) listing. + * A single object entry in a v2 ([ObjectListV2Response]) listing. * * @property name the object name within its folder. * @property key the full object key, when reported. @@ -236,7 +236,7 @@ public data class ObjectListV2Folder( * @property nextCursor cursor to pass back for the next page, when [hasNext]. */ @Serializable -public data class ObjectListV2Result( +public data class ObjectListV2Response( val hasNext: Boolean, val folders: List, val objects: List, diff --git a/supabase-storage/src/commonTest/kotlin/io/github/androidpoet/supabase/storage/StorageClientExtTest.kt b/supabase-storage/src/commonTest/kotlin/io/github/androidpoet/supabase/storage/StorageClientExtTest.kt index 0e36f9b..c6a4a55 100644 --- a/supabase-storage/src/commonTest/kotlin/io/github/androidpoet/supabase/storage/StorageClientExtTest.kt +++ b/supabase-storage/src/commonTest/kotlin/io/github/androidpoet/supabase/storage/StorageClientExtTest.kt @@ -4,7 +4,7 @@ import io.github.androidpoet.supabase.core.result.SupabaseResult import io.github.androidpoet.supabase.storage.models.AnalyticsBucket import io.github.androidpoet.supabase.storage.models.Bucket import io.github.androidpoet.supabase.storage.models.FileObject -import io.github.androidpoet.supabase.storage.models.ObjectListV2Result +import io.github.androidpoet.supabase.storage.models.ObjectListV2Response import io.github.androidpoet.supabase.storage.models.VectorBucket import io.github.androidpoet.supabase.storage.models.VectorBucketListResponse import io.github.androidpoet.supabase.storage.models.VectorData @@ -511,7 +511,7 @@ private class FakeStorageClient : StorageClient { withDelimiter: Boolean?, sortBy: String?, sortOrder: SortOrder, - ): SupabaseResult = error("not used") + ): SupabaseResult = error("not used") override suspend fun move(bucket: String, fromPath: String, toPath: String, destinationBucket: String?): SupabaseResult = error("not used") diff --git a/supabase-sync-core/api/supabase-sync-core.api b/supabase-sync-core/api/supabase-sync-core.api index 43cdab9..7e8ae74 100644 --- a/supabase-sync-core/api/supabase-sync-core.api +++ b/supabase-sync-core/api/supabase-sync-core.api @@ -7,26 +7,12 @@ public final class io/github/androidpoet/supabase/sync/ChangeKind : java/lang/En } public abstract interface class io/github/androidpoet/supabase/sync/ConflictResolver { - public abstract fun resolve (Lio/github/androidpoet/supabase/sync/Record;Lio/github/androidpoet/supabase/sync/Record;)Lio/github/androidpoet/supabase/sync/Record; -} - -public final class io/github/androidpoet/supabase/sync/Cursor { - public fun (JLjava/lang/String;)V - public synthetic fun (JLjava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun component1 ()J - public final fun component2 ()Ljava/lang/String; - public final fun copy (JLjava/lang/String;)Lio/github/androidpoet/supabase/sync/Cursor; - public static synthetic fun copy$default (Lio/github/androidpoet/supabase/sync/Cursor;JLjava/lang/String;ILjava/lang/Object;)Lio/github/androidpoet/supabase/sync/Cursor; - public fun equals (Ljava/lang/Object;)Z - public final fun getId ()Ljava/lang/String; - public final fun getUpdatedAt ()J - public fun hashCode ()I - public fun toString ()Ljava/lang/String; + public abstract fun resolve (Lio/github/androidpoet/supabase/sync/SyncRecord;Lio/github/androidpoet/supabase/sync/SyncRecord;)Lio/github/androidpoet/supabase/sync/SyncRecord; } public final class io/github/androidpoet/supabase/sync/LastWriteWins : io/github/androidpoet/supabase/sync/ConflictResolver { public static final field INSTANCE Lio/github/androidpoet/supabase/sync/LastWriteWins; - public fun resolve (Lio/github/androidpoet/supabase/sync/Record;Lio/github/androidpoet/supabase/sync/Record;)Lio/github/androidpoet/supabase/sync/Record; + public fun resolve (Lio/github/androidpoet/supabase/sync/SyncRecord;Lio/github/androidpoet/supabase/sync/SyncRecord;)Lio/github/androidpoet/supabase/sync/SyncRecord; } public abstract interface class io/github/androidpoet/supabase/sync/LocalStore { @@ -38,7 +24,7 @@ public abstract interface class io/github/androidpoet/supabase/sync/LocalStore { public abstract fun page (Ljava/lang/String;JJLkotlin/coroutines/Continuation;)Ljava/lang/Object; public abstract fun pageAfter (Ljava/lang/String;Ljava/lang/String;JLkotlin/coroutines/Continuation;)Ljava/lang/Object; public abstract fun pending (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public abstract fun setCursor (Ljava/lang/String;Lio/github/androidpoet/supabase/sync/Cursor;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun setCursor (Ljava/lang/String;Lio/github/androidpoet/supabase/sync/SyncCursor;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public abstract fun upsert (Ljava/lang/String;Ljava/util/List;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } @@ -61,14 +47,14 @@ public final class io/github/androidpoet/supabase/sync/Page { } public final class io/github/androidpoet/supabase/sync/PendingChange { - public fun (Lio/github/androidpoet/supabase/sync/Record;Lio/github/androidpoet/supabase/sync/ChangeKind;)V - public final fun component1 ()Lio/github/androidpoet/supabase/sync/Record; + public fun (Lio/github/androidpoet/supabase/sync/SyncRecord;Lio/github/androidpoet/supabase/sync/ChangeKind;)V + public final fun component1 ()Lio/github/androidpoet/supabase/sync/SyncRecord; public final fun component2 ()Lio/github/androidpoet/supabase/sync/ChangeKind; - public final fun copy (Lio/github/androidpoet/supabase/sync/Record;Lio/github/androidpoet/supabase/sync/ChangeKind;)Lio/github/androidpoet/supabase/sync/PendingChange; - public static synthetic fun copy$default (Lio/github/androidpoet/supabase/sync/PendingChange;Lio/github/androidpoet/supabase/sync/Record;Lio/github/androidpoet/supabase/sync/ChangeKind;ILjava/lang/Object;)Lio/github/androidpoet/supabase/sync/PendingChange; + public final fun copy (Lio/github/androidpoet/supabase/sync/SyncRecord;Lio/github/androidpoet/supabase/sync/ChangeKind;)Lio/github/androidpoet/supabase/sync/PendingChange; + public static synthetic fun copy$default (Lio/github/androidpoet/supabase/sync/PendingChange;Lio/github/androidpoet/supabase/sync/SyncRecord;Lio/github/androidpoet/supabase/sync/ChangeKind;ILjava/lang/Object;)Lio/github/androidpoet/supabase/sync/PendingChange; public fun equals (Ljava/lang/Object;)Z public final fun getKind ()Lio/github/androidpoet/supabase/sync/ChangeKind; - public final fun getRecord ()Lio/github/androidpoet/supabase/sync/Record; + public final fun getRecord ()Lio/github/androidpoet/supabase/sync/SyncRecord; public fun hashCode ()I public fun toString ()Ljava/lang/String; } @@ -87,14 +73,14 @@ public final class io/github/androidpoet/supabase/sync/PullProgress { } public final class io/github/androidpoet/supabase/sync/PullResult { - public fun (Ljava/util/List;Lio/github/androidpoet/supabase/sync/Cursor;)V + public fun (Ljava/util/List;Lio/github/androidpoet/supabase/sync/SyncCursor;)V public final fun component1 ()Ljava/util/List; - public final fun component2 ()Lio/github/androidpoet/supabase/sync/Cursor; - public final fun copy (Ljava/util/List;Lio/github/androidpoet/supabase/sync/Cursor;)Lio/github/androidpoet/supabase/sync/PullResult; - public static synthetic fun copy$default (Lio/github/androidpoet/supabase/sync/PullResult;Ljava/util/List;Lio/github/androidpoet/supabase/sync/Cursor;ILjava/lang/Object;)Lio/github/androidpoet/supabase/sync/PullResult; + public final fun component2 ()Lio/github/androidpoet/supabase/sync/SyncCursor; + public final fun copy (Ljava/util/List;Lio/github/androidpoet/supabase/sync/SyncCursor;)Lio/github/androidpoet/supabase/sync/PullResult; + public static synthetic fun copy$default (Lio/github/androidpoet/supabase/sync/PullResult;Ljava/util/List;Lio/github/androidpoet/supabase/sync/SyncCursor;ILjava/lang/Object;)Lio/github/androidpoet/supabase/sync/PullResult; public fun equals (Ljava/lang/Object;)Z public final fun getChanged ()Ljava/util/List; - public final fun getNextCursor ()Lio/github/androidpoet/supabase/sync/Cursor; + public final fun getNextCursor ()Lio/github/androidpoet/supabase/sync/SyncCursor; public fun hashCode ()I public fun toString ()Ljava/lang/String; } @@ -113,27 +99,9 @@ public final class io/github/androidpoet/supabase/sync/PushResult { public fun toString ()Ljava/lang/String; } -public final class io/github/androidpoet/supabase/sync/Record { - public fun (Ljava/lang/String;JZLkotlinx/serialization/json/JsonObject;)V - public synthetic fun (Ljava/lang/String;JZLkotlinx/serialization/json/JsonObject;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun component1 ()Ljava/lang/String; - public final fun component2 ()J - public final fun component3 ()Z - public final fun component4 ()Lkotlinx/serialization/json/JsonObject; - public final fun copy (Ljava/lang/String;JZLkotlinx/serialization/json/JsonObject;)Lio/github/androidpoet/supabase/sync/Record; - public static synthetic fun copy$default (Lio/github/androidpoet/supabase/sync/Record;Ljava/lang/String;JZLkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/github/androidpoet/supabase/sync/Record; - public fun equals (Ljava/lang/Object;)Z - public final fun getDeleted ()Z - public final fun getFields ()Lkotlinx/serialization/json/JsonObject; - public final fun getId ()Ljava/lang/String; - public final fun getUpdatedAt ()J - public fun hashCode ()I - public fun toString ()Ljava/lang/String; -} - public abstract interface class io/github/androidpoet/supabase/sync/RemoteSource { public abstract fun changes (Ljava/lang/String;)Lkotlinx/coroutines/flow/Flow; - public abstract fun pull (Ljava/lang/String;Lio/github/androidpoet/supabase/sync/Cursor;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun pull (Ljava/lang/String;Lio/github/androidpoet/supabase/sync/SyncCursor;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public abstract fun push (Ljava/lang/String;Ljava/util/List;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } @@ -145,6 +113,20 @@ public final class io/github/androidpoet/supabase/sync/ResolverRegistry { public final fun register (Ljava/lang/String;Lio/github/androidpoet/supabase/sync/ConflictResolver;)Lio/github/androidpoet/supabase/sync/ResolverRegistry; } +public final class io/github/androidpoet/supabase/sync/SyncCursor { + public fun (JLjava/lang/String;)V + public synthetic fun (JLjava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()J + public final fun component2 ()Ljava/lang/String; + public final fun copy (JLjava/lang/String;)Lio/github/androidpoet/supabase/sync/SyncCursor; + public static synthetic fun copy$default (Lio/github/androidpoet/supabase/sync/SyncCursor;JLjava/lang/String;ILjava/lang/Object;)Lio/github/androidpoet/supabase/sync/SyncCursor; + public fun equals (Ljava/lang/Object;)Z + public final fun getId ()Ljava/lang/String; + public final fun getUpdatedAt ()J + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + public final class io/github/androidpoet/supabase/sync/SyncEngine { public fun (Lio/github/androidpoet/supabase/sync/LocalStore;Lio/github/androidpoet/supabase/sync/RemoteSource;Lio/github/androidpoet/supabase/sync/ResolverRegistry;)V public synthetic fun (Lio/github/androidpoet/supabase/sync/LocalStore;Lio/github/androidpoet/supabase/sync/RemoteSource;Lio/github/androidpoet/supabase/sync/ResolverRegistry;ILkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -158,6 +140,24 @@ public final class io/github/androidpoet/supabase/sync/SyncEngineFactoryKt { public static synthetic fun createSyncEngine$default (Lio/github/androidpoet/supabase/sync/LocalStore;Lio/github/androidpoet/supabase/sync/RemoteSource;Lio/github/androidpoet/supabase/sync/ResolverRegistry;ILjava/lang/Object;)Lio/github/androidpoet/supabase/sync/SyncEngine; } +public final class io/github/androidpoet/supabase/sync/SyncRecord { + public fun (Ljava/lang/String;JZLkotlinx/serialization/json/JsonObject;)V + public synthetic fun (Ljava/lang/String;JZLkotlinx/serialization/json/JsonObject;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/lang/String; + public final fun component2 ()J + public final fun component3 ()Z + public final fun component4 ()Lkotlinx/serialization/json/JsonObject; + public final fun copy (Ljava/lang/String;JZLkotlinx/serialization/json/JsonObject;)Lio/github/androidpoet/supabase/sync/SyncRecord; + public static synthetic fun copy$default (Lio/github/androidpoet/supabase/sync/SyncRecord;Ljava/lang/String;JZLkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/github/androidpoet/supabase/sync/SyncRecord; + public fun equals (Ljava/lang/Object;)Z + public final fun getDeleted ()Z + public final fun getFields ()Lkotlinx/serialization/json/JsonObject; + public final fun getId ()Ljava/lang/String; + public final fun getUpdatedAt ()J + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + public final class io/github/androidpoet/supabase/sync/SyncResult { public fun (III)V public final fun component1 ()I diff --git a/supabase-sync-core/api/supabase-sync-core.klib.api b/supabase-sync-core/api/supabase-sync-core.klib.api index 4d5c0ef..5446c97 100644 --- a/supabase-sync-core/api/supabase-sync-core.klib.api +++ b/supabase-sync-core/api/supabase-sync-core.klib.api @@ -18,25 +18,25 @@ final enum class io.github.androidpoet.supabase.sync/ChangeKind : kotlin/Enum) // io.github.androidpoet.supabase.sync/LocalStore.clearPending|clearPending(kotlin.String;kotlin.collections.List){}[0] abstract suspend fun count(kotlin/String): kotlin/Long // io.github.androidpoet.supabase.sync/LocalStore.count|count(kotlin.String){}[0] - abstract suspend fun cursor(kotlin/String): io.github.androidpoet.supabase.sync/Cursor? // io.github.androidpoet.supabase.sync/LocalStore.cursor|cursor(kotlin.String){}[0] + abstract suspend fun cursor(kotlin/String): io.github.androidpoet.supabase.sync/SyncCursor? // io.github.androidpoet.supabase.sync/LocalStore.cursor|cursor(kotlin.String){}[0] abstract suspend fun enqueue(kotlin/String, io.github.androidpoet.supabase.sync/PendingChange) // io.github.androidpoet.supabase.sync/LocalStore.enqueue|enqueue(kotlin.String;io.github.androidpoet.supabase.sync.PendingChange){}[0] - abstract suspend fun get(kotlin/String, kotlin/String): io.github.androidpoet.supabase.sync/Record? // io.github.androidpoet.supabase.sync/LocalStore.get|get(kotlin.String;kotlin.String){}[0] - abstract suspend fun page(kotlin/String, kotlin/Long, kotlin/Long): io.github.androidpoet.supabase.sync/Page // io.github.androidpoet.supabase.sync/LocalStore.page|page(kotlin.String;kotlin.Long;kotlin.Long){}[0] - abstract suspend fun pageAfter(kotlin/String, kotlin/String?, kotlin/Long): kotlin.collections/List // io.github.androidpoet.supabase.sync/LocalStore.pageAfter|pageAfter(kotlin.String;kotlin.String?;kotlin.Long){}[0] + abstract suspend fun get(kotlin/String, kotlin/String): io.github.androidpoet.supabase.sync/SyncRecord? // io.github.androidpoet.supabase.sync/LocalStore.get|get(kotlin.String;kotlin.String){}[0] + abstract suspend fun page(kotlin/String, kotlin/Long, kotlin/Long): io.github.androidpoet.supabase.sync/Page // io.github.androidpoet.supabase.sync/LocalStore.page|page(kotlin.String;kotlin.Long;kotlin.Long){}[0] + abstract suspend fun pageAfter(kotlin/String, kotlin/String?, kotlin/Long): kotlin.collections/List // io.github.androidpoet.supabase.sync/LocalStore.pageAfter|pageAfter(kotlin.String;kotlin.String?;kotlin.Long){}[0] abstract suspend fun pending(kotlin/String): kotlin.collections/List // io.github.androidpoet.supabase.sync/LocalStore.pending|pending(kotlin.String){}[0] - abstract suspend fun setCursor(kotlin/String, io.github.androidpoet.supabase.sync/Cursor?) // io.github.androidpoet.supabase.sync/LocalStore.setCursor|setCursor(kotlin.String;io.github.androidpoet.supabase.sync.Cursor?){}[0] - abstract suspend fun upsert(kotlin/String, kotlin.collections/List) // io.github.androidpoet.supabase.sync/LocalStore.upsert|upsert(kotlin.String;kotlin.collections.List){}[0] + abstract suspend fun setCursor(kotlin/String, io.github.androidpoet.supabase.sync/SyncCursor?) // io.github.androidpoet.supabase.sync/LocalStore.setCursor|setCursor(kotlin.String;io.github.androidpoet.supabase.sync.SyncCursor?){}[0] + abstract suspend fun upsert(kotlin/String, kotlin.collections/List) // io.github.androidpoet.supabase.sync/LocalStore.upsert|upsert(kotlin.String;kotlin.collections.List){}[0] } abstract interface io.github.androidpoet.supabase.sync/RemoteSource { // io.github.androidpoet.supabase.sync/RemoteSource|null[0] - abstract fun changes(kotlin/String): kotlinx.coroutines.flow/Flow // io.github.androidpoet.supabase.sync/RemoteSource.changes|changes(kotlin.String){}[0] - abstract suspend fun pull(kotlin/String, io.github.androidpoet.supabase.sync/Cursor?): io.github.androidpoet.supabase.sync/PullResult // io.github.androidpoet.supabase.sync/RemoteSource.pull|pull(kotlin.String;io.github.androidpoet.supabase.sync.Cursor?){}[0] + abstract fun changes(kotlin/String): kotlinx.coroutines.flow/Flow // io.github.androidpoet.supabase.sync/RemoteSource.changes|changes(kotlin.String){}[0] + abstract suspend fun pull(kotlin/String, io.github.androidpoet.supabase.sync/SyncCursor?): io.github.androidpoet.supabase.sync/PullResult // io.github.androidpoet.supabase.sync/RemoteSource.pull|pull(kotlin.String;io.github.androidpoet.supabase.sync.SyncCursor?){}[0] abstract suspend fun push(kotlin/String, kotlin.collections/List): io.github.androidpoet.supabase.sync/PushResult // io.github.androidpoet.supabase.sync/RemoteSource.push|push(kotlin.String;kotlin.collections.List){}[0] } @@ -76,33 +76,17 @@ final class <#A: kotlin/Any?> io.github.androidpoet.supabase.sync/Page { // io.g final fun toString(): kotlin/String // io.github.androidpoet.supabase.sync/Page.toString|toString(){}[0] } -final class io.github.androidpoet.supabase.sync/Cursor { // io.github.androidpoet.supabase.sync/Cursor|null[0] - constructor (kotlin/Long, kotlin/String = ...) // io.github.androidpoet.supabase.sync/Cursor.|(kotlin.Long;kotlin.String){}[0] - - final val id // io.github.androidpoet.supabase.sync/Cursor.id|{}id[0] - final fun (): kotlin/String // io.github.androidpoet.supabase.sync/Cursor.id.|(){}[0] - final val updatedAt // io.github.androidpoet.supabase.sync/Cursor.updatedAt|{}updatedAt[0] - final fun (): kotlin/Long // io.github.androidpoet.supabase.sync/Cursor.updatedAt.|(){}[0] - - final fun component1(): kotlin/Long // io.github.androidpoet.supabase.sync/Cursor.component1|component1(){}[0] - final fun component2(): kotlin/String // io.github.androidpoet.supabase.sync/Cursor.component2|component2(){}[0] - final fun copy(kotlin/Long = ..., kotlin/String = ...): io.github.androidpoet.supabase.sync/Cursor // io.github.androidpoet.supabase.sync/Cursor.copy|copy(kotlin.Long;kotlin.String){}[0] - final fun equals(kotlin/Any?): kotlin/Boolean // io.github.androidpoet.supabase.sync/Cursor.equals|equals(kotlin.Any?){}[0] - final fun hashCode(): kotlin/Int // io.github.androidpoet.supabase.sync/Cursor.hashCode|hashCode(){}[0] - final fun toString(): kotlin/String // io.github.androidpoet.supabase.sync/Cursor.toString|toString(){}[0] -} - final class io.github.androidpoet.supabase.sync/PendingChange { // io.github.androidpoet.supabase.sync/PendingChange|null[0] - constructor (io.github.androidpoet.supabase.sync/Record, io.github.androidpoet.supabase.sync/ChangeKind) // io.github.androidpoet.supabase.sync/PendingChange.|(io.github.androidpoet.supabase.sync.Record;io.github.androidpoet.supabase.sync.ChangeKind){}[0] + constructor (io.github.androidpoet.supabase.sync/SyncRecord, io.github.androidpoet.supabase.sync/ChangeKind) // io.github.androidpoet.supabase.sync/PendingChange.|(io.github.androidpoet.supabase.sync.SyncRecord;io.github.androidpoet.supabase.sync.ChangeKind){}[0] final val kind // io.github.androidpoet.supabase.sync/PendingChange.kind|{}kind[0] final fun (): io.github.androidpoet.supabase.sync/ChangeKind // io.github.androidpoet.supabase.sync/PendingChange.kind.|(){}[0] final val record // io.github.androidpoet.supabase.sync/PendingChange.record|{}record[0] - final fun (): io.github.androidpoet.supabase.sync/Record // io.github.androidpoet.supabase.sync/PendingChange.record.|(){}[0] + final fun (): io.github.androidpoet.supabase.sync/SyncRecord // io.github.androidpoet.supabase.sync/PendingChange.record.|(){}[0] - final fun component1(): io.github.androidpoet.supabase.sync/Record // io.github.androidpoet.supabase.sync/PendingChange.component1|component1(){}[0] + final fun component1(): io.github.androidpoet.supabase.sync/SyncRecord // io.github.androidpoet.supabase.sync/PendingChange.component1|component1(){}[0] final fun component2(): io.github.androidpoet.supabase.sync/ChangeKind // io.github.androidpoet.supabase.sync/PendingChange.component2|component2(){}[0] - final fun copy(io.github.androidpoet.supabase.sync/Record = ..., io.github.androidpoet.supabase.sync/ChangeKind = ...): io.github.androidpoet.supabase.sync/PendingChange // io.github.androidpoet.supabase.sync/PendingChange.copy|copy(io.github.androidpoet.supabase.sync.Record;io.github.androidpoet.supabase.sync.ChangeKind){}[0] + final fun copy(io.github.androidpoet.supabase.sync/SyncRecord = ..., io.github.androidpoet.supabase.sync/ChangeKind = ...): io.github.androidpoet.supabase.sync/PendingChange // io.github.androidpoet.supabase.sync/PendingChange.copy|copy(io.github.androidpoet.supabase.sync.SyncRecord;io.github.androidpoet.supabase.sync.ChangeKind){}[0] final fun equals(kotlin/Any?): kotlin/Boolean // io.github.androidpoet.supabase.sync/PendingChange.equals|equals(kotlin.Any?){}[0] final fun hashCode(): kotlin/Int // io.github.androidpoet.supabase.sync/PendingChange.hashCode|hashCode(){}[0] final fun toString(): kotlin/String // io.github.androidpoet.supabase.sync/PendingChange.toString|toString(){}[0] @@ -125,16 +109,16 @@ final class io.github.androidpoet.supabase.sync/PullProgress { // io.github.andr } final class io.github.androidpoet.supabase.sync/PullResult { // io.github.androidpoet.supabase.sync/PullResult|null[0] - constructor (kotlin.collections/List, io.github.androidpoet.supabase.sync/Cursor?) // io.github.androidpoet.supabase.sync/PullResult.|(kotlin.collections.List;io.github.androidpoet.supabase.sync.Cursor?){}[0] + constructor (kotlin.collections/List, io.github.androidpoet.supabase.sync/SyncCursor?) // io.github.androidpoet.supabase.sync/PullResult.|(kotlin.collections.List;io.github.androidpoet.supabase.sync.SyncCursor?){}[0] final val changed // io.github.androidpoet.supabase.sync/PullResult.changed|{}changed[0] - final fun (): kotlin.collections/List // io.github.androidpoet.supabase.sync/PullResult.changed.|(){}[0] + final fun (): kotlin.collections/List // io.github.androidpoet.supabase.sync/PullResult.changed.|(){}[0] final val nextCursor // io.github.androidpoet.supabase.sync/PullResult.nextCursor|{}nextCursor[0] - final fun (): io.github.androidpoet.supabase.sync/Cursor? // io.github.androidpoet.supabase.sync/PullResult.nextCursor.|(){}[0] + final fun (): io.github.androidpoet.supabase.sync/SyncCursor? // io.github.androidpoet.supabase.sync/PullResult.nextCursor.|(){}[0] - final fun component1(): kotlin.collections/List // io.github.androidpoet.supabase.sync/PullResult.component1|component1(){}[0] - final fun component2(): io.github.androidpoet.supabase.sync/Cursor? // io.github.androidpoet.supabase.sync/PullResult.component2|component2(){}[0] - final fun copy(kotlin.collections/List = ..., io.github.androidpoet.supabase.sync/Cursor? = ...): io.github.androidpoet.supabase.sync/PullResult // io.github.androidpoet.supabase.sync/PullResult.copy|copy(kotlin.collections.List;io.github.androidpoet.supabase.sync.Cursor?){}[0] + final fun component1(): kotlin.collections/List // io.github.androidpoet.supabase.sync/PullResult.component1|component1(){}[0] + final fun component2(): io.github.androidpoet.supabase.sync/SyncCursor? // io.github.androidpoet.supabase.sync/PullResult.component2|component2(){}[0] + final fun copy(kotlin.collections/List = ..., io.github.androidpoet.supabase.sync/SyncCursor? = ...): io.github.androidpoet.supabase.sync/PullResult // io.github.androidpoet.supabase.sync/PullResult.copy|copy(kotlin.collections.List;io.github.androidpoet.supabase.sync.SyncCursor?){}[0] final fun equals(kotlin/Any?): kotlin/Boolean // io.github.androidpoet.supabase.sync/PullResult.equals|equals(kotlin.Any?){}[0] final fun hashCode(): kotlin/Int // io.github.androidpoet.supabase.sync/PullResult.hashCode|hashCode(){}[0] final fun toString(): kotlin/String // io.github.androidpoet.supabase.sync/PullResult.toString|toString(){}[0] @@ -156,28 +140,6 @@ final class io.github.androidpoet.supabase.sync/PushResult { // io.github.androi final fun toString(): kotlin/String // io.github.androidpoet.supabase.sync/PushResult.toString|toString(){}[0] } -final class io.github.androidpoet.supabase.sync/Record { // io.github.androidpoet.supabase.sync/Record|null[0] - constructor (kotlin/String, kotlin/Long, kotlin/Boolean = ..., kotlinx.serialization.json/JsonObject) // io.github.androidpoet.supabase.sync/Record.|(kotlin.String;kotlin.Long;kotlin.Boolean;kotlinx.serialization.json.JsonObject){}[0] - - final val deleted // io.github.androidpoet.supabase.sync/Record.deleted|{}deleted[0] - final fun (): kotlin/Boolean // io.github.androidpoet.supabase.sync/Record.deleted.|(){}[0] - final val fields // io.github.androidpoet.supabase.sync/Record.fields|{}fields[0] - final fun (): kotlinx.serialization.json/JsonObject // io.github.androidpoet.supabase.sync/Record.fields.|(){}[0] - final val id // io.github.androidpoet.supabase.sync/Record.id|{}id[0] - final fun (): kotlin/String // io.github.androidpoet.supabase.sync/Record.id.|(){}[0] - final val updatedAt // io.github.androidpoet.supabase.sync/Record.updatedAt|{}updatedAt[0] - final fun (): kotlin/Long // io.github.androidpoet.supabase.sync/Record.updatedAt.|(){}[0] - - final fun component1(): kotlin/String // io.github.androidpoet.supabase.sync/Record.component1|component1(){}[0] - final fun component2(): kotlin/Long // io.github.androidpoet.supabase.sync/Record.component2|component2(){}[0] - final fun component3(): kotlin/Boolean // io.github.androidpoet.supabase.sync/Record.component3|component3(){}[0] - final fun component4(): kotlinx.serialization.json/JsonObject // io.github.androidpoet.supabase.sync/Record.component4|component4(){}[0] - final fun copy(kotlin/String = ..., kotlin/Long = ..., kotlin/Boolean = ..., kotlinx.serialization.json/JsonObject = ...): io.github.androidpoet.supabase.sync/Record // io.github.androidpoet.supabase.sync/Record.copy|copy(kotlin.String;kotlin.Long;kotlin.Boolean;kotlinx.serialization.json.JsonObject){}[0] - final fun equals(kotlin/Any?): kotlin/Boolean // io.github.androidpoet.supabase.sync/Record.equals|equals(kotlin.Any?){}[0] - final fun hashCode(): kotlin/Int // io.github.androidpoet.supabase.sync/Record.hashCode|hashCode(){}[0] - final fun toString(): kotlin/String // io.github.androidpoet.supabase.sync/Record.toString|toString(){}[0] -} - final class io.github.androidpoet.supabase.sync/ResolverRegistry { // io.github.androidpoet.supabase.sync/ResolverRegistry|null[0] constructor (io.github.androidpoet.supabase.sync/ConflictResolver = ...) // io.github.androidpoet.supabase.sync/ResolverRegistry.|(io.github.androidpoet.supabase.sync.ConflictResolver){}[0] @@ -185,14 +147,52 @@ final class io.github.androidpoet.supabase.sync/ResolverRegistry { // io.github. final fun register(kotlin/String, io.github.androidpoet.supabase.sync/ConflictResolver): io.github.androidpoet.supabase.sync/ResolverRegistry // io.github.androidpoet.supabase.sync/ResolverRegistry.register|register(kotlin.String;io.github.androidpoet.supabase.sync.ConflictResolver){}[0] } +final class io.github.androidpoet.supabase.sync/SyncCursor { // io.github.androidpoet.supabase.sync/SyncCursor|null[0] + constructor (kotlin/Long, kotlin/String = ...) // io.github.androidpoet.supabase.sync/SyncCursor.|(kotlin.Long;kotlin.String){}[0] + + final val id // io.github.androidpoet.supabase.sync/SyncCursor.id|{}id[0] + final fun (): kotlin/String // io.github.androidpoet.supabase.sync/SyncCursor.id.|(){}[0] + final val updatedAt // io.github.androidpoet.supabase.sync/SyncCursor.updatedAt|{}updatedAt[0] + final fun (): kotlin/Long // io.github.androidpoet.supabase.sync/SyncCursor.updatedAt.|(){}[0] + + final fun component1(): kotlin/Long // io.github.androidpoet.supabase.sync/SyncCursor.component1|component1(){}[0] + final fun component2(): kotlin/String // io.github.androidpoet.supabase.sync/SyncCursor.component2|component2(){}[0] + final fun copy(kotlin/Long = ..., kotlin/String = ...): io.github.androidpoet.supabase.sync/SyncCursor // io.github.androidpoet.supabase.sync/SyncCursor.copy|copy(kotlin.Long;kotlin.String){}[0] + final fun equals(kotlin/Any?): kotlin/Boolean // io.github.androidpoet.supabase.sync/SyncCursor.equals|equals(kotlin.Any?){}[0] + final fun hashCode(): kotlin/Int // io.github.androidpoet.supabase.sync/SyncCursor.hashCode|hashCode(){}[0] + final fun toString(): kotlin/String // io.github.androidpoet.supabase.sync/SyncCursor.toString|toString(){}[0] +} + final class io.github.androidpoet.supabase.sync/SyncEngine { // io.github.androidpoet.supabase.sync/SyncEngine|null[0] constructor (io.github.androidpoet.supabase.sync/LocalStore, io.github.androidpoet.supabase.sync/RemoteSource, io.github.androidpoet.supabase.sync/ResolverRegistry = ...) // io.github.androidpoet.supabase.sync/SyncEngine.|(io.github.androidpoet.supabase.sync.LocalStore;io.github.androidpoet.supabase.sync.RemoteSource;io.github.androidpoet.supabase.sync.ResolverRegistry){}[0] - final fun observe(kotlin/String): kotlinx.coroutines.flow/Flow // io.github.androidpoet.supabase.sync/SyncEngine.observe|observe(kotlin.String){}[0] + final fun observe(kotlin/String): kotlinx.coroutines.flow/Flow // io.github.androidpoet.supabase.sync/SyncEngine.observe|observe(kotlin.String){}[0] final suspend fun pullPage(kotlin/String): io.github.androidpoet.supabase.sync/PullProgress // io.github.androidpoet.supabase.sync/SyncEngine.pullPage|pullPage(kotlin.String){}[0] final suspend fun sync(kotlin/String): io.github.androidpoet.supabase.sync/SyncResult // io.github.androidpoet.supabase.sync/SyncEngine.sync|sync(kotlin.String){}[0] } +final class io.github.androidpoet.supabase.sync/SyncRecord { // io.github.androidpoet.supabase.sync/SyncRecord|null[0] + constructor (kotlin/String, kotlin/Long, kotlin/Boolean = ..., kotlinx.serialization.json/JsonObject) // io.github.androidpoet.supabase.sync/SyncRecord.|(kotlin.String;kotlin.Long;kotlin.Boolean;kotlinx.serialization.json.JsonObject){}[0] + + final val deleted // io.github.androidpoet.supabase.sync/SyncRecord.deleted|{}deleted[0] + final fun (): kotlin/Boolean // io.github.androidpoet.supabase.sync/SyncRecord.deleted.|(){}[0] + final val fields // io.github.androidpoet.supabase.sync/SyncRecord.fields|{}fields[0] + final fun (): kotlinx.serialization.json/JsonObject // io.github.androidpoet.supabase.sync/SyncRecord.fields.|(){}[0] + final val id // io.github.androidpoet.supabase.sync/SyncRecord.id|{}id[0] + final fun (): kotlin/String // io.github.androidpoet.supabase.sync/SyncRecord.id.|(){}[0] + final val updatedAt // io.github.androidpoet.supabase.sync/SyncRecord.updatedAt|{}updatedAt[0] + final fun (): kotlin/Long // io.github.androidpoet.supabase.sync/SyncRecord.updatedAt.|(){}[0] + + final fun component1(): kotlin/String // io.github.androidpoet.supabase.sync/SyncRecord.component1|component1(){}[0] + final fun component2(): kotlin/Long // io.github.androidpoet.supabase.sync/SyncRecord.component2|component2(){}[0] + final fun component3(): kotlin/Boolean // io.github.androidpoet.supabase.sync/SyncRecord.component3|component3(){}[0] + final fun component4(): kotlinx.serialization.json/JsonObject // io.github.androidpoet.supabase.sync/SyncRecord.component4|component4(){}[0] + final fun copy(kotlin/String = ..., kotlin/Long = ..., kotlin/Boolean = ..., kotlinx.serialization.json/JsonObject = ...): io.github.androidpoet.supabase.sync/SyncRecord // io.github.androidpoet.supabase.sync/SyncRecord.copy|copy(kotlin.String;kotlin.Long;kotlin.Boolean;kotlinx.serialization.json.JsonObject){}[0] + final fun equals(kotlin/Any?): kotlin/Boolean // io.github.androidpoet.supabase.sync/SyncRecord.equals|equals(kotlin.Any?){}[0] + final fun hashCode(): kotlin/Int // io.github.androidpoet.supabase.sync/SyncRecord.hashCode|hashCode(){}[0] + final fun toString(): kotlin/String // io.github.androidpoet.supabase.sync/SyncRecord.toString|toString(){}[0] +} + final class io.github.androidpoet.supabase.sync/SyncResult { // io.github.androidpoet.supabase.sync/SyncResult|null[0] constructor (kotlin/Int, kotlin/Int, kotlin/Int) // io.github.androidpoet.supabase.sync/SyncResult.|(kotlin.Int;kotlin.Int;kotlin.Int){}[0] @@ -213,7 +213,7 @@ final class io.github.androidpoet.supabase.sync/SyncResult { // io.github.androi } final object io.github.androidpoet.supabase.sync/LastWriteWins : io.github.androidpoet.supabase.sync/ConflictResolver { // io.github.androidpoet.supabase.sync/LastWriteWins|null[0] - final fun resolve(io.github.androidpoet.supabase.sync/Record, io.github.androidpoet.supabase.sync/Record): io.github.androidpoet.supabase.sync/Record // io.github.androidpoet.supabase.sync/LastWriteWins.resolve|resolve(io.github.androidpoet.supabase.sync.Record;io.github.androidpoet.supabase.sync.Record){}[0] + final fun resolve(io.github.androidpoet.supabase.sync/SyncRecord, io.github.androidpoet.supabase.sync/SyncRecord): io.github.androidpoet.supabase.sync/SyncRecord // io.github.androidpoet.supabase.sync/LastWriteWins.resolve|resolve(io.github.androidpoet.supabase.sync.SyncRecord;io.github.androidpoet.supabase.sync.SyncRecord){}[0] } final fun io.github.androidpoet.supabase.sync/createSyncEngine(io.github.androidpoet.supabase.sync/LocalStore, io.github.androidpoet.supabase.sync/RemoteSource, io.github.androidpoet.supabase.sync/ResolverRegistry = ...): io.github.androidpoet.supabase.sync/SyncEngine // io.github.androidpoet.supabase.sync/createSyncEngine|createSyncEngine(io.github.androidpoet.supabase.sync.LocalStore;io.github.androidpoet.supabase.sync.RemoteSource;io.github.androidpoet.supabase.sync.ResolverRegistry){}[0] diff --git a/supabase-sync-core/src/commonMain/kotlin/io/github/androidpoet/supabase/sync/ConflictResolver.kt b/supabase-sync-core/src/commonMain/kotlin/io/github/androidpoet/supabase/sync/ConflictResolver.kt index e19d4a9..55f92cd 100644 --- a/supabase-sync-core/src/commonMain/kotlin/io/github/androidpoet/supabase/sync/ConflictResolver.kt +++ b/supabase-sync-core/src/commonMain/kotlin/io/github/androidpoet/supabase/sync/ConflictResolver.kt @@ -6,12 +6,12 @@ package io.github.androidpoet.supabase.sync * [ResolverRegistry] to do field-level merges or any other policy your app needs. */ public fun interface ConflictResolver { - public fun resolve(local: Record, remote: Record): Record + public fun resolve(local: SyncRecord, remote: SyncRecord): SyncRecord } -/** Higher [Record.updatedAt] wins; ties go to the remote, since the server is authoritative. */ +/** Higher [SyncRecord.updatedAt] wins; ties go to the remote, since the server is authoritative. */ public object LastWriteWins : ConflictResolver { - override fun resolve(local: Record, remote: Record): Record = + override fun resolve(local: SyncRecord, remote: SyncRecord): SyncRecord = if (local.updatedAt > remote.updatedAt) local else remote } diff --git a/supabase-sync-core/src/commonMain/kotlin/io/github/androidpoet/supabase/sync/Model.kt b/supabase-sync-core/src/commonMain/kotlin/io/github/androidpoet/supabase/sync/Model.kt index 9cb4576..892f416 100644 --- a/supabase-sync-core/src/commonMain/kotlin/io/github/androidpoet/supabase/sync/Model.kt +++ b/supabase-sync-core/src/commonMain/kotlin/io/github/androidpoet/supabase/sync/Model.kt @@ -8,7 +8,7 @@ import kotlinx.serialization.json.JsonObject * tombstone so deletions propagate through an incremental pull (a hard delete is invisible to * a "changed since" query). */ -public data class Record( +public data class SyncRecord( public val id: String, public val updatedAt: Long, public val deleted: Boolean = false, @@ -23,7 +23,7 @@ public data class Record( * cursor` skips rows written in the same millisecond at the boundary, while `>=` re-fetches or * loops. [id] defaults to empty (the low bound), so a cursor of just an `updatedAt` still works. */ -public data class Cursor( +public data class SyncCursor( public val updatedAt: Long, public val id: String = "", ) @@ -33,14 +33,14 @@ public enum class ChangeKind { UPSERT, DELETE } /** A local mutation waiting to be pushed to the remote. */ public data class PendingChange( - public val record: Record, + public val record: SyncRecord, public val kind: ChangeKind, ) /** What a pull returned: the rows that changed plus the cursor to resume from next time. */ public data class PullResult( - public val changed: List, - public val nextCursor: Cursor?, + public val changed: List, + public val nextCursor: SyncCursor?, ) /** Per-id outcome of a push: which local changes the server accepted vs rejected. */ diff --git a/supabase-sync-core/src/commonMain/kotlin/io/github/androidpoet/supabase/sync/Stores.kt b/supabase-sync-core/src/commonMain/kotlin/io/github/androidpoet/supabase/sync/Stores.kt index 22208a4..51629e2 100644 --- a/supabase-sync-core/src/commonMain/kotlin/io/github/androidpoet/supabase/sync/Stores.kt +++ b/supabase-sync-core/src/commonMain/kotlin/io/github/androidpoet/supabase/sync/Stores.kt @@ -15,16 +15,16 @@ public interface LocalStore { * with a client clock that trails the row's stored (server) `updatedAt` would be silently * dropped by that guard — local edits go through [enqueue] instead. */ - public suspend fun upsert(table: String, records: List) + public suspend fun upsert(table: String, records: List) /** The row [id] in [table], or `null` if absent. */ - public suspend fun get(table: String, id: String): Record? + public suspend fun get(table: String, id: String): SyncRecord? /** The last pull position stored for [table], or `null` if it has never synced. */ - public suspend fun cursor(table: String): Cursor? + public suspend fun cursor(table: String): SyncCursor? /** Persists the pull position for [table]. */ - public suspend fun setCursor(table: String, cursor: Cursor?) + public suspend fun setCursor(table: String, cursor: SyncCursor?) /** Local changes for [table] still waiting to be pushed. */ public suspend fun pending(table: String): List @@ -42,10 +42,10 @@ public interface LocalStore { public suspend fun clearPending(table: String, ids: List) /** Offset page of live (non-tombstone) rows in [table], ordered by id. Drives `SyncStore.paged`. */ - public suspend fun page(table: String, limit: Long, offset: Long): Page + public suspend fun page(table: String, limit: Long, offset: Long): Page /** Keyset page: the next [limit] live rows after [afterId] (`null` = first page), ordered by id. */ - public suspend fun pageAfter(table: String, afterId: String?, limit: Long): List + public suspend fun pageAfter(table: String, afterId: String?, limit: Long): List /** Total live (non-tombstone) row count for [table]. */ public suspend fun count(table: String): Long @@ -62,11 +62,11 @@ public interface RemoteSource { * the position to resume from, or `null` to leave the stored cursor unchanged (e.g. when * nothing changed) — returning `null` must never force a full re-sync. */ - public suspend fun pull(table: String, since: Cursor?): PullResult + public suspend fun pull(table: String, since: SyncCursor?): PullResult /** Sends local [changes] to the server and reports which were accepted. */ public suspend fun push(table: String, changes: List): PushResult - /** A live stream of remote changes for [table] (e.g. Supabase Realtime), one [Record] per change. */ - public fun changes(table: String): Flow + /** A live stream of remote changes for [table] (e.g. Supabase Realtime), one [SyncRecord] per change. */ + public fun changes(table: String): Flow } diff --git a/supabase-sync-core/src/commonMain/kotlin/io/github/androidpoet/supabase/sync/SyncEngine.kt b/supabase-sync-core/src/commonMain/kotlin/io/github/androidpoet/supabase/sync/SyncEngine.kt index bfd1233..e0df304 100644 --- a/supabase-sync-core/src/commonMain/kotlin/io/github/androidpoet/supabase/sync/SyncEngine.kt +++ b/supabase-sync-core/src/commonMain/kotlin/io/github/androidpoet/supabase/sync/SyncEngine.kt @@ -61,7 +61,7 @@ public class SyncEngine( val pendingById = local.pending(table).associateBy { it.record.id } val resolver = resolvers.forTable(table) - val toStore = ArrayList(pull.changed.size) + val toStore = ArrayList(pull.changed.size) val clearedConflicts = mutableListOf() val reEnqueue = mutableListOf() @@ -112,7 +112,7 @@ public class SyncEngine( val MAX_PULL_PAGES = 10_000 } - public fun observe(table: String): Flow = + public fun observe(table: String): Flow = remote.changes(table).onEach { incoming -> val localPending = local.pending(table).firstOrNull { it.record.id == incoming.id }?.record val winner = diff --git a/supabase-sync-core/src/commonTest/kotlin/io/github/androidpoet/supabase/sync/SyncEngineTest.kt b/supabase-sync-core/src/commonTest/kotlin/io/github/androidpoet/supabase/sync/SyncEngineTest.kt index 631ad7a..efb65d9 100644 --- a/supabase-sync-core/src/commonTest/kotlin/io/github/androidpoet/supabase/sync/SyncEngineTest.kt +++ b/supabase-sync-core/src/commonTest/kotlin/io/github/androidpoet/supabase/sync/SyncEngineTest.kt @@ -12,19 +12,19 @@ import kotlin.test.assertTrue class SyncEngineTest { private fun rec(id: String, ts: Long, deleted: Boolean = false) = - Record(id = id, updatedAt = ts, deleted = deleted, fields = JsonObject(mapOf("v" to JsonPrimitive(ts)))) + SyncRecord(id = id, updatedAt = ts, deleted = deleted, fields = JsonObject(mapOf("v" to JsonPrimitive(ts)))) @Test fun pull_stores_records_and_advances_the_cursor() = runTest { val local = FakeLocalStore() - val remote = FakeRemoteSource(PullResult(listOf(rec("a", 10), rec("b", 20)), Cursor(20))) + val remote = FakeRemoteSource(PullResult(listOf(rec("a", 10), rec("b", 20)), SyncCursor(20))) val result = SyncEngine(local, remote).sync("todos") assertEquals(2, result.pulled) assertEquals(rec("a", 10), local.get("todos", "a")) - assertEquals(Cursor(20), local.cursor("todos")) + assertEquals(SyncCursor(20), local.cursor("todos")) } @Test @@ -46,7 +46,7 @@ class SyncEngineTest { runTest { val local = FakeLocalStore() local.enqueue("todos", PendingChange(rec("a", 100), ChangeKind.UPSERT)) - val remote = FakeRemoteSource(PullResult(listOf(rec("a", 50)), Cursor(50))) + val remote = FakeRemoteSource(PullResult(listOf(rec("a", 50)), SyncCursor(50))) val result = SyncEngine(local, remote).sync("todos") @@ -65,7 +65,7 @@ class SyncEngineTest { runTest { val local = FakeLocalStore() local.enqueue("todos", PendingChange(rec("a", 50), ChangeKind.UPSERT)) - val remote = FakeRemoteSource(PullResult(listOf(rec("a", 100)), Cursor(100))) + val remote = FakeRemoteSource(PullResult(listOf(rec("a", 100)), SyncCursor(100))) val result = SyncEngine(local, remote).sync("todos") @@ -81,7 +81,7 @@ class SyncEngineTest { val resolvers = ResolverRegistry().register("todos") { _, _ -> merged } val local = FakeLocalStore() local.enqueue("todos", PendingChange(rec("a", 60), ChangeKind.UPSERT)) - val remote = FakeRemoteSource(PullResult(listOf(rec("a", 50)), Cursor(50))) + val remote = FakeRemoteSource(PullResult(listOf(rec("a", 50)), SyncCursor(50))) val result = SyncEngine(local, remote, resolvers).sync("todos") @@ -95,12 +95,12 @@ class SyncEngineTest { fun empty_pull_with_null_cursor_keeps_the_existing_cursor() = runTest { val local = FakeLocalStore() - local.setCursor("todos", Cursor(99)) + local.setCursor("todos", SyncCursor(99)) val remote = FakeRemoteSource(PullResult(emptyList(), null)) SyncEngine(local, remote).sync("todos") - assertEquals(Cursor(99), local.cursor("todos")) // not reset to null + assertEquals(SyncCursor(99), local.cursor("todos")) // not reset to null } @Test @@ -118,20 +118,20 @@ class SyncEngineTest { } private class FakeLocalStore : LocalStore { - private val tables = mutableMapOf>() - private val cursors = mutableMapOf() + private val tables = mutableMapOf>() + private val cursors = mutableMapOf() private val outbox = mutableMapOf>() - override suspend fun upsert(table: String, records: List) { + override suspend fun upsert(table: String, records: List) { val t = tables.getOrPut(table) { mutableMapOf() } records.forEach { t[it.id] = it } } - override suspend fun get(table: String, id: String): Record? = tables[table]?.get(id) + override suspend fun get(table: String, id: String): SyncRecord? = tables[table]?.get(id) - override suspend fun cursor(table: String): Cursor? = cursors[table] + override suspend fun cursor(table: String): SyncCursor? = cursors[table] - override suspend fun setCursor(table: String, cursor: Cursor?) { + override suspend fun setCursor(table: String, cursor: SyncCursor?) { cursors[table] = cursor } @@ -150,15 +150,15 @@ private class FakeLocalStore : LocalStore { outbox[table]?.removeAll { it.record.id in ids } } - private fun live(table: String): List = + private fun live(table: String): List = tables[table]?.values?.filterNot { it.deleted }?.sortedBy { it.id } ?: emptyList() - override suspend fun page(table: String, limit: Long, offset: Long): Page { + override suspend fun page(table: String, limit: Long, offset: Long): Page { val all = live(table) return Page(all.drop(offset.toInt()).take(limit.toInt()), offset, limit, all.size.toLong()) } - override suspend fun pageAfter(table: String, afterId: String?, limit: Long): List = + override suspend fun pageAfter(table: String, afterId: String?, limit: Long): List = live(table).filter { afterId == null || it.id > afterId }.take(limit.toInt()) override suspend fun count(table: String): Long = live(table).size.toLong() @@ -169,14 +169,14 @@ private class FakeRemoteSource( ) : RemoteSource { val pushed = mutableListOf() private var pullIndex = 0 - private var changeFlow: Flow = emptyFlow() + private var changeFlow: Flow = emptyFlow() - fun withChanges(vararg changes: Record): FakeRemoteSource { + fun withChanges(vararg changes: SyncRecord): FakeRemoteSource { changeFlow = flowOf(*changes) return this } - override suspend fun pull(table: String, since: Cursor?): PullResult = + override suspend fun pull(table: String, since: SyncCursor?): PullResult = scriptedPulls.getOrNull(pullIndex++) ?: PullResult(emptyList(), since) override suspend fun push(table: String, changes: List): PushResult { @@ -184,5 +184,5 @@ private class FakeRemoteSource( return PushResult(accepted = changes.map { it.record.id }) } - override fun changes(table: String): Flow = changeFlow + override fun changes(table: String): Flow = changeFlow } diff --git a/supabase-sync-paging/src/commonMain/kotlin/io/github/androidpoet/supabase/sync/paging/SyncStore.kt b/supabase-sync-paging/src/commonMain/kotlin/io/github/androidpoet/supabase/sync/paging/SyncStore.kt index b0ebd96..c3668ed 100644 --- a/supabase-sync-paging/src/commonMain/kotlin/io/github/androidpoet/supabase/sync/paging/SyncStore.kt +++ b/supabase-sync-paging/src/commonMain/kotlin/io/github/androidpoet/supabase/sync/paging/SyncStore.kt @@ -17,8 +17,8 @@ import app.cash.paging.RemoteMediatorMediatorResultSuccess import io.github.androidpoet.supabase.sync.ChangeKind import io.github.androidpoet.supabase.sync.LocalStore import io.github.androidpoet.supabase.sync.PendingChange -import io.github.androidpoet.supabase.sync.Record import io.github.androidpoet.supabase.sync.SyncEngine +import io.github.androidpoet.supabase.sync.SyncRecord import io.github.androidpoet.supabase.sync.SyncResult import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.channels.Channel @@ -104,7 +104,7 @@ public class SyncStore( /** Optimistically insert/replace [value]: visible to reads at once, queued to push next sync. */ public suspend fun upsert(value: T) { - val record = Record(idOf(value), now(), deleted = false, fields = encode(value)) + val record = SyncRecord(idOf(value), now(), deleted = false, fields = encode(value)) // Local writes go through enqueue (the optimistic path), NOT local.upsert: upsert carries the // remote monotonic guard, which would silently drop this edit whenever the client clock is // behind the row's stored (server) updatedAt. enqueue applies the row and queues it atomically. @@ -116,7 +116,7 @@ public class SyncStore( /** Optimistically delete [id] via a soft-delete tombstone, queued to push next sync. */ public suspend fun delete(id: String) { val existing = local.get(table, id) - val record = Record(id, now(), deleted = true, fields = existing?.fields ?: EMPTY_FIELDS) + val record = SyncRecord(id, now(), deleted = true, fields = existing?.fields ?: EMPTY_FIELDS) // Route through enqueue (not local.upsert), same as [upsert]: enqueue writes the tombstone and // queues it atomically, unguarded — local.upsert's remote monotonic guard could otherwise drop // the delete when the client clock trails the row's stored updatedAt. @@ -174,7 +174,7 @@ public class SyncStore( revision.update { it + 1 } } - private fun decode(record: Record): T = json.decodeFromJsonElement(serializer, record.fields) + private fun decode(record: SyncRecord): T = json.decodeFromJsonElement(serializer, record.fields) // `.jsonObject` throws a clear IllegalArgumentException if T doesn't serialize to a JSON object // (a row model always does); friendlier than a raw ClassCastException. @@ -204,7 +204,7 @@ public val DefaultJson: Json = private class LocalPagingSource( private val table: String, private val local: LocalStore, - private val decode: (Record) -> T, + private val decode: (SyncRecord) -> T, revision: StateFlow, scope: CoroutineScope, ) : PagingSource() { diff --git a/supabase-sync-paging/src/commonTest/kotlin/io/github/androidpoet/supabase/sync/paging/SyncStoreTest.kt b/supabase-sync-paging/src/commonTest/kotlin/io/github/androidpoet/supabase/sync/paging/SyncStoreTest.kt index 1a247a8..3e53680 100644 --- a/supabase-sync-paging/src/commonTest/kotlin/io/github/androidpoet/supabase/sync/paging/SyncStoreTest.kt +++ b/supabase-sync-paging/src/commonTest/kotlin/io/github/androidpoet/supabase/sync/paging/SyncStoreTest.kt @@ -1,15 +1,15 @@ package io.github.androidpoet.supabase.sync.paging import io.github.androidpoet.supabase.sync.ChangeKind -import io.github.androidpoet.supabase.sync.Cursor import io.github.androidpoet.supabase.sync.LocalStore import io.github.androidpoet.supabase.sync.Page import io.github.androidpoet.supabase.sync.PendingChange import io.github.androidpoet.supabase.sync.PullResult import io.github.androidpoet.supabase.sync.PushResult -import io.github.androidpoet.supabase.sync.Record import io.github.androidpoet.supabase.sync.RemoteSource +import io.github.androidpoet.supabase.sync.SyncCursor import io.github.androidpoet.supabase.sync.SyncEngine +import io.github.androidpoet.supabase.sync.SyncRecord import kotlinx.coroutines.flow.first import kotlinx.coroutines.test.runTest import kotlinx.serialization.Serializable @@ -83,7 +83,7 @@ class SyncStoreTest { fun pullPage_drains_pages_until_empty() = runTest { val (local, engine, remote) = testEnv() - remote.seed("notes", List(3) { Record("$it", it.toLong(), false, fieldsOf("$it")) }) + remote.seed("notes", List(3) { SyncRecord("$it", it.toLong(), false, fieldsOf("$it")) }) val first = engine.pullPage("notes") assertEquals(3, first.pulled) @@ -99,7 +99,7 @@ class SyncStoreTest { // Remote paginates 2 rows at a time; a single sync() must pull all 5, not just page 1. val local = MemLocalStore() val remote = MemRemote(pageSize = 2) - remote.seed("notes", List(5) { Record("id$it", it.toLong(), false, fieldsOf("id$it")) }) + remote.seed("notes", List(5) { SyncRecord("id$it", it.toLong(), false, fieldsOf("id$it")) }) val engine = SyncEngine(local, remote) val result = engine.sync("notes") @@ -114,20 +114,20 @@ private fun fieldsOf(id: String) = /** Minimal in-memory [LocalStore] for tests — mirrors the SQLDelight store's contract. */ private class MemLocalStore : LocalStore { - private val rows = mutableMapOf>() - private val cursors = mutableMapOf() + private val rows = mutableMapOf>() + private val cursors = mutableMapOf() private val outbox = mutableMapOf>() - override suspend fun upsert(table: String, records: List) { + override suspend fun upsert(table: String, records: List) { val t = rows.getOrPut(table) { mutableMapOf() } records.forEach { t[it.id] = it } } - override suspend fun get(table: String, id: String): Record? = rows[table]?.get(id) + override suspend fun get(table: String, id: String): SyncRecord? = rows[table]?.get(id) - override suspend fun cursor(table: String): Cursor? = cursors[table] + override suspend fun cursor(table: String): SyncCursor? = cursors[table] - override suspend fun setCursor(table: String, cursor: Cursor?) { + override suspend fun setCursor(table: String, cursor: SyncCursor?) { cursors[table] = cursor } @@ -148,12 +148,12 @@ private class MemLocalStore : LocalStore { private fun live(table: String) = rows[table]?.values?.filterNot { it.deleted }?.sortedBy { it.id } ?: emptyList() - override suspend fun page(table: String, limit: Long, offset: Long): Page { + override suspend fun page(table: String, limit: Long, offset: Long): Page { val all = live(table) return Page(all.drop(offset.toInt()).take(limit.toInt()), offset, limit, all.size.toLong()) } - override suspend fun pageAfter(table: String, afterId: String?, limit: Long): List = + override suspend fun pageAfter(table: String, afterId: String?, limit: Long): List = live(table).filter { afterId == null || it.id > afterId }.take(limit.toInt()) override suspend fun count(table: String): Long = live(table).size.toLong() @@ -163,24 +163,24 @@ private class MemLocalStore : LocalStore { private class MemRemote( private val pageSize: Int = Int.MAX_VALUE, ) : RemoteSource { - private val seeded = mutableMapOf>() + private val seeded = mutableMapOf>() - fun seed(table: String, records: List) { + fun seed(table: String, records: List) { seeded[table] = records.sortedWith(compareBy({ it.updatedAt }, { it.id })) } - override suspend fun pull(table: String, since: Cursor?): PullResult { + override suspend fun pull(table: String, since: SyncCursor?): PullResult { val all = seeded[table].orEmpty() val rows = all .filter { since == null || it.updatedAt > since.updatedAt || (it.updatedAt == since.updatedAt && it.id > since.id) } .take(pageSize) - val next = rows.lastOrNull()?.let { Cursor(it.updatedAt, it.id) } + val next = rows.lastOrNull()?.let { SyncCursor(it.updatedAt, it.id) } return PullResult(rows, next) } override suspend fun push(table: String, changes: List): PushResult = PushResult(accepted = changes.map { it.record.id }) - override fun changes(table: String) = kotlinx.coroutines.flow.emptyFlow() + override fun changes(table: String) = kotlinx.coroutines.flow.emptyFlow() } diff --git a/supabase-sync-sqldelight/api/supabase-sync-sqldelight.api b/supabase-sync-sqldelight/api/supabase-sync-sqldelight.api index 98cd6c1..434095e 100644 --- a/supabase-sync-sqldelight/api/supabase-sync-sqldelight.api +++ b/supabase-sync-sqldelight/api/supabase-sync-sqldelight.api @@ -36,7 +36,7 @@ public final class io/github/androidpoet/supabase/sync/store/SqlDelightLocalStor public fun page (Ljava/lang/String;JJLkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun pageAfter (Ljava/lang/String;Ljava/lang/String;JLkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun pending (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public fun setCursor (Ljava/lang/String;Lio/github/androidpoet/supabase/sync/Cursor;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun setCursor (Ljava/lang/String;Lio/github/androidpoet/supabase/sync/SyncCursor;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun upsert (Ljava/lang/String;Ljava/util/List;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } diff --git a/supabase-sync-sqldelight/api/supabase-sync-sqldelight.klib.api b/supabase-sync-sqldelight/api/supabase-sync-sqldelight.klib.api index e1b9b37..ed6d3c8 100644 --- a/supabase-sync-sqldelight/api/supabase-sync-sqldelight.klib.api +++ b/supabase-sync-sqldelight/api/supabase-sync-sqldelight.klib.api @@ -44,14 +44,14 @@ final class io.github.androidpoet.supabase.sync.store/SqlDelightLocalStore : io. final suspend fun clearPending(kotlin/String, kotlin.collections/List) // io.github.androidpoet.supabase.sync.store/SqlDelightLocalStore.clearPending|clearPending(kotlin.String;kotlin.collections.List){}[0] final suspend fun count(kotlin/String): kotlin/Long // io.github.androidpoet.supabase.sync.store/SqlDelightLocalStore.count|count(kotlin.String){}[0] - final suspend fun cursor(kotlin/String): io.github.androidpoet.supabase.sync/Cursor? // io.github.androidpoet.supabase.sync.store/SqlDelightLocalStore.cursor|cursor(kotlin.String){}[0] + final suspend fun cursor(kotlin/String): io.github.androidpoet.supabase.sync/SyncCursor? // io.github.androidpoet.supabase.sync.store/SqlDelightLocalStore.cursor|cursor(kotlin.String){}[0] final suspend fun enqueue(kotlin/String, io.github.androidpoet.supabase.sync/PendingChange) // io.github.androidpoet.supabase.sync.store/SqlDelightLocalStore.enqueue|enqueue(kotlin.String;io.github.androidpoet.supabase.sync.PendingChange){}[0] - final suspend fun get(kotlin/String, kotlin/String): io.github.androidpoet.supabase.sync/Record? // io.github.androidpoet.supabase.sync.store/SqlDelightLocalStore.get|get(kotlin.String;kotlin.String){}[0] - final suspend fun page(kotlin/String, kotlin/Long, kotlin/Long): io.github.androidpoet.supabase.sync/Page // io.github.androidpoet.supabase.sync.store/SqlDelightLocalStore.page|page(kotlin.String;kotlin.Long;kotlin.Long){}[0] - final suspend fun pageAfter(kotlin/String, kotlin/String?, kotlin/Long): kotlin.collections/List // io.github.androidpoet.supabase.sync.store/SqlDelightLocalStore.pageAfter|pageAfter(kotlin.String;kotlin.String?;kotlin.Long){}[0] + final suspend fun get(kotlin/String, kotlin/String): io.github.androidpoet.supabase.sync/SyncRecord? // io.github.androidpoet.supabase.sync.store/SqlDelightLocalStore.get|get(kotlin.String;kotlin.String){}[0] + final suspend fun page(kotlin/String, kotlin/Long, kotlin/Long): io.github.androidpoet.supabase.sync/Page // io.github.androidpoet.supabase.sync.store/SqlDelightLocalStore.page|page(kotlin.String;kotlin.Long;kotlin.Long){}[0] + final suspend fun pageAfter(kotlin/String, kotlin/String?, kotlin/Long): kotlin.collections/List // io.github.androidpoet.supabase.sync.store/SqlDelightLocalStore.pageAfter|pageAfter(kotlin.String;kotlin.String?;kotlin.Long){}[0] final suspend fun pending(kotlin/String): kotlin.collections/List // io.github.androidpoet.supabase.sync.store/SqlDelightLocalStore.pending|pending(kotlin.String){}[0] - final suspend fun setCursor(kotlin/String, io.github.androidpoet.supabase.sync/Cursor?) // io.github.androidpoet.supabase.sync.store/SqlDelightLocalStore.setCursor|setCursor(kotlin.String;io.github.androidpoet.supabase.sync.Cursor?){}[0] - final suspend fun upsert(kotlin/String, kotlin.collections/List) // io.github.androidpoet.supabase.sync.store/SqlDelightLocalStore.upsert|upsert(kotlin.String;kotlin.collections.List){}[0] + final suspend fun setCursor(kotlin/String, io.github.androidpoet.supabase.sync/SyncCursor?) // io.github.androidpoet.supabase.sync.store/SqlDelightLocalStore.setCursor|setCursor(kotlin.String;io.github.androidpoet.supabase.sync.SyncCursor?){}[0] + final suspend fun upsert(kotlin/String, kotlin.collections/List) // io.github.androidpoet.supabase.sync.store/SqlDelightLocalStore.upsert|upsert(kotlin.String;kotlin.collections.List){}[0] } final class io.github.androidpoet.supabase.sync.store/SqlDelightTableAdapter : io.github.androidpoet.supabase.sync/TableAdapter { // io.github.androidpoet.supabase.sync.store/SqlDelightTableAdapter|null[0] diff --git a/supabase-sync-sqldelight/src/commonMain/kotlin/io/github/androidpoet/supabase/sync/store/SqlDelightLocalStore.kt b/supabase-sync-sqldelight/src/commonMain/kotlin/io/github/androidpoet/supabase/sync/store/SqlDelightLocalStore.kt index d7b8156..dcec570 100644 --- a/supabase-sync-sqldelight/src/commonMain/kotlin/io/github/androidpoet/supabase/sync/store/SqlDelightLocalStore.kt +++ b/supabase-sync-sqldelight/src/commonMain/kotlin/io/github/androidpoet/supabase/sync/store/SqlDelightLocalStore.kt @@ -2,11 +2,11 @@ package io.github.androidpoet.supabase.sync.store import app.cash.sqldelight.db.SqlDriver import io.github.androidpoet.supabase.sync.ChangeKind -import io.github.androidpoet.supabase.sync.Cursor import io.github.androidpoet.supabase.sync.LocalStore import io.github.androidpoet.supabase.sync.Page import io.github.androidpoet.supabase.sync.PendingChange -import io.github.androidpoet.supabase.sync.Record +import io.github.androidpoet.supabase.sync.SyncCursor +import io.github.androidpoet.supabase.sync.SyncRecord import io.github.androidpoet.supabase.sync.TableAdapter import io.github.androidpoet.supabase.sync.store.db.OfflineSyncDb import kotlinx.serialization.json.Json @@ -39,7 +39,7 @@ public class SqlDelightLocalStore internal constructor( // --- remote rows landing locally (these become what get()/page() read) --- - override suspend fun upsert(table: String, records: List) { + override suspend fun upsert(table: String, records: List) { if (records.isEmpty()) return val adapter = adapterFor(table) db.transaction { @@ -53,10 +53,10 @@ public class SqlDelightLocalStore internal constructor( } } - override suspend fun get(table: String, id: String): Record? { + override suspend fun get(table: String, id: String): SyncRecord? { val adapter = adapterFor(table) ?: return blobGet(table, id) val meta = db.syncMetaQueries.get(table, id).executeAsOneOrNull() ?: return null - return Record( + return SyncRecord( id = id, updatedAt = meta.updatedAt, deleted = meta.deleted.toBoolean(), @@ -66,13 +66,13 @@ public class SqlDelightLocalStore internal constructor( // --- pull cursor (always generic) --- - override suspend fun cursor(table: String): Cursor? = + override suspend fun cursor(table: String): SyncCursor? = db.syncCursorQueries .cursor(table) .executeAsOneOrNull() - ?.let { Cursor(it.updatedAt, it.id) } + ?.let { SyncCursor(it.updatedAt, it.id) } - override suspend fun setCursor(table: String, cursor: Cursor?) { + override suspend fun setCursor(table: String, cursor: SyncCursor?) { if (cursor == null) { db.syncCursorQueries.clearCursor(table) } else { @@ -85,7 +85,7 @@ public class SqlDelightLocalStore internal constructor( override suspend fun pending(table: String): List = db.outboxQueries.pending(table).executeAsList().map { row -> PendingChange( - record = Record(row.id, row.updatedAt, row.deleted.toBoolean(), decode(row.fields)), + record = SyncRecord(row.id, row.updatedAt, row.deleted.toBoolean(), decode(row.fields)), kind = ChangeKind.valueOf(row.kind), ) } @@ -120,7 +120,7 @@ public class SqlDelightLocalStore internal constructor( // --- pagination: three list slots (offset page / keyset page / count) --- /** Offset pagination — a [Page] of [limit] rows starting at [offset], with the table's total. */ - override suspend fun page(table: String, limit: Long, offset: Long): Page { + override suspend fun page(table: String, limit: Long, offset: Long): Page { val adapter = adapterFor(table) if (adapter != null) { val items = adapter.page(limit, offset).map { (id, fields) -> typedRecord(table, id, fields) } @@ -130,12 +130,12 @@ public class SqlDelightLocalStore internal constructor( db.syncRecordQueries .page(table, limit, offset) .executeAsList() - .map { Record(it.id, it.updatedAt, it.deleted.toBoolean(), decode(it.fields)) } + .map { SyncRecord(it.id, it.updatedAt, it.deleted.toBoolean(), decode(it.fields)) } return Page(items, offset, limit, db.syncRecordQueries.countAll(table).executeAsOne()) } /** Keyset pagination — the next [limit] rows after [afterId] (`null` = first page). */ - override suspend fun pageAfter(table: String, afterId: String?, limit: Long): List { + override suspend fun pageAfter(table: String, afterId: String?, limit: Long): List { val adapter = adapterFor(table) if (adapter != null) { return adapter.pageAfter(afterId, limit).map { (id, fields) -> typedRecord(table, id, fields) } @@ -143,7 +143,7 @@ public class SqlDelightLocalStore internal constructor( return db.syncRecordQueries .pageAfter(table, afterId ?: "", limit) .executeAsList() - .map { Record(it.id, it.updatedAt, it.deleted.toBoolean(), decode(it.fields)) } + .map { SyncRecord(it.id, it.updatedAt, it.deleted.toBoolean(), decode(it.fields)) } } /** Total row count for [table] (drives [Page.total] / "page x of y"). */ @@ -166,23 +166,23 @@ public class SqlDelightLocalStore internal constructor( ?.updatedAt ?: Long.MIN_VALUE } - private fun applyToTyped(adapter: TableAdapter, table: String, record: Record) { + private fun applyToTyped(adapter: TableAdapter, table: String, record: SyncRecord) { if (record.deleted) adapter.delete(record.id) else adapter.upsert(record.id, record.fields) db.syncMetaQueries.put(table, record.id, record.updatedAt, record.deleted.toLong()) } - private fun blobUpsert(table: String, record: Record) { + private fun blobUpsert(table: String, record: SyncRecord) { db.syncRecordQueries.upsert(table, record.id, record.updatedAt, record.deleted.toLong(), encode(record.fields)) } - private fun blobGet(table: String, id: String): Record? = + private fun blobGet(table: String, id: String): SyncRecord? = db.syncRecordQueries.selectById(table, id).executeAsOneOrNull()?.let { - Record(id, it.updatedAt, it.deleted.toBoolean(), decode(it.fields)) + SyncRecord(id, it.updatedAt, it.deleted.toBoolean(), decode(it.fields)) } - private fun typedRecord(table: String, id: String, fields: JsonObject): Record { + private fun typedRecord(table: String, id: String, fields: JsonObject): SyncRecord { val meta = db.syncMetaQueries.get(table, id).executeAsOneOrNull() - return Record(id, meta?.updatedAt ?: 0L, meta?.deleted.toBoolean(), fields) + return SyncRecord(id, meta?.updatedAt ?: 0L, meta?.deleted.toBoolean(), fields) } private fun encode(fields: JsonObject): String = json.encodeToString(JsonObject.serializer(), fields) diff --git a/supabase-sync-sqldelight/src/commonTest/kotlin/io/github/androidpoet/supabase/sync/store/SqlDelightLocalStoreTest.kt b/supabase-sync-sqldelight/src/commonTest/kotlin/io/github/androidpoet/supabase/sync/store/SqlDelightLocalStoreTest.kt index a0d77f6..925863b 100644 --- a/supabase-sync-sqldelight/src/commonTest/kotlin/io/github/androidpoet/supabase/sync/store/SqlDelightLocalStoreTest.kt +++ b/supabase-sync-sqldelight/src/commonTest/kotlin/io/github/androidpoet/supabase/sync/store/SqlDelightLocalStoreTest.kt @@ -1,9 +1,9 @@ package io.github.androidpoet.supabase.sync.store import io.github.androidpoet.supabase.sync.ChangeKind -import io.github.androidpoet.supabase.sync.Cursor import io.github.androidpoet.supabase.sync.PendingChange -import io.github.androidpoet.supabase.sync.Record +import io.github.androidpoet.supabase.sync.SyncCursor +import io.github.androidpoet.supabase.sync.SyncRecord import kotlinx.coroutines.test.runTest import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.JsonPrimitive @@ -21,8 +21,8 @@ class SqlDelightLocalStoreTest { @AfterTest fun tearDown() = driver.close() - private fun record(id: String, updatedAt: Long, deleted: Boolean = false, title: String = "t"): Record = - Record( + private fun record(id: String, updatedAt: Long, deleted: Boolean = false, title: String = "t"): SyncRecord = + SyncRecord( id = id, updatedAt = updatedAt, deleted = deleted, @@ -67,8 +67,8 @@ class SqlDelightLocalStoreTest { runTest { assertNull(store.cursor("todos")) - store.setCursor("todos", Cursor(updatedAt = 42, id = "msg-7")) - assertEquals(Cursor(42, "msg-7"), store.cursor("todos"), "both halves of the keyset round-trip") + store.setCursor("todos", SyncCursor(updatedAt = 42, id = "msg-7")) + assertEquals(SyncCursor(42, "msg-7"), store.cursor("todos"), "both halves of the keyset round-trip") assertNull(store.cursor("notes"), "cursors don't bleed across tables") store.setCursor("todos", null) diff --git a/supabase-sync-sqldelight/src/commonTest/kotlin/io/github/androidpoet/supabase/sync/store/SqlDelightTableAdapterTest.kt b/supabase-sync-sqldelight/src/commonTest/kotlin/io/github/androidpoet/supabase/sync/store/SqlDelightTableAdapterTest.kt index e130360..549008d 100644 --- a/supabase-sync-sqldelight/src/commonTest/kotlin/io/github/androidpoet/supabase/sync/store/SqlDelightTableAdapterTest.kt +++ b/supabase-sync-sqldelight/src/commonTest/kotlin/io/github/androidpoet/supabase/sync/store/SqlDelightTableAdapterTest.kt @@ -1,6 +1,6 @@ package io.github.androidpoet.supabase.sync.store -import io.github.androidpoet.supabase.sync.Record +import io.github.androidpoet.supabase.sync.SyncRecord import kotlinx.coroutines.test.runTest import kotlinx.serialization.json.JsonNull import kotlinx.serialization.json.JsonObject @@ -95,7 +95,7 @@ class SqlDelightTableAdapterTest { fun works_as_the_stores_source_of_truth() = runTest { val store = SqlDelightLocalStore(driver, adapters = mapOf("todos" to adapter)) - store.upsert("todos", listOf(Record("a", updatedAt = 7, fields = row("a", "synced", 1)))) + store.upsert("todos", listOf(SyncRecord("a", updatedAt = 7, fields = row("a", "synced", 1)))) val got = store.get("todos", "a") assertEquals(7, got?.updatedAt) diff --git a/supabase-sync-sqldelight/src/commonTest/kotlin/io/github/androidpoet/supabase/sync/store/SsotAndPaginationTest.kt b/supabase-sync-sqldelight/src/commonTest/kotlin/io/github/androidpoet/supabase/sync/store/SsotAndPaginationTest.kt index 77baf89..69870df 100644 --- a/supabase-sync-sqldelight/src/commonTest/kotlin/io/github/androidpoet/supabase/sync/store/SsotAndPaginationTest.kt +++ b/supabase-sync-sqldelight/src/commonTest/kotlin/io/github/androidpoet/supabase/sync/store/SsotAndPaginationTest.kt @@ -2,7 +2,7 @@ package io.github.androidpoet.supabase.sync.store import io.github.androidpoet.supabase.sync.ChangeKind import io.github.androidpoet.supabase.sync.PendingChange -import io.github.androidpoet.supabase.sync.Record +import io.github.androidpoet.supabase.sync.SyncRecord import kotlinx.coroutines.test.runTest import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.JsonPrimitive @@ -21,8 +21,8 @@ class SsotAndPaginationTest { @AfterTest fun tearDown() = driver.close() - private fun record(id: String, updatedAt: Long, deleted: Boolean = false, title: String = "t"): Record = - Record(id, updatedAt, deleted, buildJsonObject { put("title", JsonPrimitive(title)) }) + private fun record(id: String, updatedAt: Long, deleted: Boolean = false, title: String = "t"): SyncRecord = + SyncRecord(id, updatedAt, deleted, buildJsonObject { put("title", JsonPrimitive(title)) }) private fun JsonObject.title(): String? = (this["title"] as? JsonPrimitive)?.content diff --git a/supabase-sync/api/supabase-sync.api b/supabase-sync/api/supabase-sync.api index 2d3554f..f68376b 100644 --- a/supabase-sync/api/supabase-sync.api +++ b/supabase-sync/api/supabase-sync.api @@ -2,7 +2,7 @@ public final class io/github/androidpoet/supabase/sync/remote/SupabaseRemoteSour public fun (Lio/github/androidpoet/supabase/database/DatabaseClient;Lio/github/androidpoet/supabase/realtime/RealtimeClient;Lio/github/androidpoet/supabase/sync/remote/SyncColumns;Ljava/lang/String;I)V public synthetic fun (Lio/github/androidpoet/supabase/database/DatabaseClient;Lio/github/androidpoet/supabase/realtime/RealtimeClient;Lio/github/androidpoet/supabase/sync/remote/SyncColumns;Ljava/lang/String;IILkotlin/jvm/internal/DefaultConstructorMarker;)V public fun changes (Ljava/lang/String;)Lkotlinx/coroutines/flow/Flow; - public fun pull (Ljava/lang/String;Lio/github/androidpoet/supabase/sync/Cursor;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun pull (Ljava/lang/String;Lio/github/androidpoet/supabase/sync/SyncCursor;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun push (Ljava/lang/String;Ljava/util/List;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } diff --git a/supabase-sync/api/supabase-sync.klib.api b/supabase-sync/api/supabase-sync.klib.api index 4c14a18..8e435cc 100644 --- a/supabase-sync/api/supabase-sync.klib.api +++ b/supabase-sync/api/supabase-sync.klib.api @@ -9,8 +9,8 @@ final class io.github.androidpoet.supabase.sync.remote/SupabaseRemoteSource : io.github.androidpoet.supabase.sync/RemoteSource { // io.github.androidpoet.supabase.sync.remote/SupabaseRemoteSource|null[0] constructor (io.github.androidpoet.supabase.database/DatabaseClient, io.github.androidpoet.supabase.realtime/RealtimeClient, io.github.androidpoet.supabase.sync.remote/SyncColumns = ..., kotlin/String = ..., kotlin/Int = ...) // io.github.androidpoet.supabase.sync.remote/SupabaseRemoteSource.|(io.github.androidpoet.supabase.database.DatabaseClient;io.github.androidpoet.supabase.realtime.RealtimeClient;io.github.androidpoet.supabase.sync.remote.SyncColumns;kotlin.String;kotlin.Int){}[0] - final fun changes(kotlin/String): kotlinx.coroutines.flow/Flow // io.github.androidpoet.supabase.sync.remote/SupabaseRemoteSource.changes|changes(kotlin.String){}[0] - final suspend fun pull(kotlin/String, io.github.androidpoet.supabase.sync/Cursor?): io.github.androidpoet.supabase.sync/PullResult // io.github.androidpoet.supabase.sync.remote/SupabaseRemoteSource.pull|pull(kotlin.String;io.github.androidpoet.supabase.sync.Cursor?){}[0] + final fun changes(kotlin/String): kotlinx.coroutines.flow/Flow // io.github.androidpoet.supabase.sync.remote/SupabaseRemoteSource.changes|changes(kotlin.String){}[0] + final suspend fun pull(kotlin/String, io.github.androidpoet.supabase.sync/SyncCursor?): io.github.androidpoet.supabase.sync/PullResult // io.github.androidpoet.supabase.sync.remote/SupabaseRemoteSource.pull|pull(kotlin.String;io.github.androidpoet.supabase.sync.SyncCursor?){}[0] final suspend fun push(kotlin/String, kotlin.collections/List): io.github.androidpoet.supabase.sync/PushResult // io.github.androidpoet.supabase.sync.remote/SupabaseRemoteSource.push|push(kotlin.String;kotlin.collections.List){}[0] } diff --git a/supabase-sync/src/commonMain/kotlin/io/github/androidpoet/supabase/sync/remote/Mapping.kt b/supabase-sync/src/commonMain/kotlin/io/github/androidpoet/supabase/sync/remote/Mapping.kt index cf1f2c3..f5f48ab 100644 --- a/supabase-sync/src/commonMain/kotlin/io/github/androidpoet/supabase/sync/remote/Mapping.kt +++ b/supabase-sync/src/commonMain/kotlin/io/github/androidpoet/supabase/sync/remote/Mapping.kt @@ -1,7 +1,7 @@ package io.github.androidpoet.supabase.sync.remote -import io.github.androidpoet.supabase.sync.Cursor -import io.github.androidpoet.supabase.sync.Record +import io.github.androidpoet.supabase.sync.SyncCursor +import io.github.androidpoet.supabase.sync.SyncRecord import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonArray import kotlinx.serialization.json.JsonObject @@ -13,27 +13,27 @@ import kotlinx.serialization.json.jsonObject import kotlinx.serialization.json.jsonPrimitive import kotlinx.serialization.json.longOrNull -// The pure JSON <-> Record translation used by SupabaseRemoteSource. Kept free of any HTTP or +// The pure JSON <-> SyncRecord translation used by SupabaseRemoteSource. Kept free of any HTTP or // realtime types so the tricky parts — keyset cursor, metadata stripping, push body shape — are // unit-testable without a live Supabase. /** - * Turns one PostgREST/Realtime row into a [Record]: pulls out [SyncColumns.id], + * Turns one PostgREST/Realtime row into a [SyncRecord]: pulls out [SyncColumns.id], * [SyncColumns.updatedAt] and [SyncColumns.deleted], and keeps everything else (including the id - * column) as the domain [Record.fields] that mirror the typed local table. [forceDeleted] marks + * column) as the domain [SyncRecord.fields] that mirror the typed local table. [forceDeleted] marks * the record deleted regardless of the column — used for a hard `DELETE` realtime event whose old * row carries no tombstone. */ -internal fun rowToRecord(row: JsonObject, columns: SyncColumns, forceDeleted: Boolean = false): Record { +internal fun rowToRecord(row: JsonObject, columns: SyncColumns, forceDeleted: Boolean = false): SyncRecord { val id = (row[columns.id] ?: error("row is missing id column '${columns.id}'")).jsonPrimitive.content val updatedAt = row[columns.updatedAt]?.jsonPrimitive?.longOrNull ?: 0L val deleted = forceDeleted || (row[columns.deleted]?.jsonPrimitive?.booleanOrNull ?: false) val fields = JsonObject(row.filterKeys { it != columns.updatedAt && it != columns.deleted }) - return Record(id = id, updatedAt = updatedAt, deleted = deleted, fields = fields) + return SyncRecord(id = id, updatedAt = updatedAt, deleted = deleted, fields = fields) } -/** Parses a PostgREST select body (a JSON array, possibly empty/null) into [Record]s. */ -internal fun parseRows(body: String, columns: SyncColumns): List { +/** Parses a PostgREST select body (a JSON array, possibly empty/null) into [SyncRecord]s. */ +internal fun parseRows(body: String, columns: SyncColumns): List { val trimmed = body.trim() if (trimmed.isEmpty() || trimmed == "null") return emptyList() return Json.parseToJsonElement(trimmed).jsonArray.map { rowToRecord(it.jsonObject, columns) } @@ -44,14 +44,14 @@ internal fun parseRows(body: String, columns: SyncColumns): List { * a reset — so an empty page does not force a full re-sync. The high-water mark is the max * `(updatedAt, id)` seen, computed defensively rather than trusting row order. */ -internal fun pullCursor(records: List): Cursor? { +internal fun pullCursor(records: List): SyncCursor? { if (records.isEmpty()) return null val last = records.maxWith(compareBy({ it.updatedAt }, { it.id })) - return Cursor(last.updatedAt, last.id) + return SyncCursor(last.updatedAt, last.id) } -/** Reconstructs the full row to upsert: domain [Record.fields] plus the sync-metadata columns. */ -internal fun recordToRow(record: Record, columns: SyncColumns): JsonObject = +/** Reconstructs the full row to upsert: domain [SyncRecord.fields] plus the sync-metadata columns. */ +internal fun recordToRow(record: SyncRecord, columns: SyncColumns): JsonObject = buildJsonObject { record.fields.forEach { (key, value) -> put(key, value) } if (!record.fields.containsKey(columns.id)) put(columns.id, JsonPrimitive(record.id)) @@ -60,5 +60,5 @@ internal fun recordToRow(record: Record, columns: SyncColumns): JsonObject = } /** Encodes outbox records as the JSON array body of a bulk PostgREST upsert. */ -internal fun encodeRows(records: List, columns: SyncColumns): String = +internal fun encodeRows(records: List, columns: SyncColumns): String = JsonArray(records.map { recordToRow(it, columns) }).toString() diff --git a/supabase-sync/src/commonMain/kotlin/io/github/androidpoet/supabase/sync/remote/SupabaseRemoteSource.kt b/supabase-sync/src/commonMain/kotlin/io/github/androidpoet/supabase/sync/remote/SupabaseRemoteSource.kt index c1326e0..b3e82b5 100644 --- a/supabase-sync/src/commonMain/kotlin/io/github/androidpoet/supabase/sync/remote/SupabaseRemoteSource.kt +++ b/supabase-sync/src/commonMain/kotlin/io/github/androidpoet/supabase/sync/remote/SupabaseRemoteSource.kt @@ -6,12 +6,12 @@ import io.github.androidpoet.supabase.database.DatabaseClient import io.github.androidpoet.supabase.database.ReturnOption import io.github.androidpoet.supabase.realtime.RealtimeClient import io.github.androidpoet.supabase.realtime.models.PostgresChangeEvent -import io.github.androidpoet.supabase.sync.Cursor import io.github.androidpoet.supabase.sync.PendingChange import io.github.androidpoet.supabase.sync.PullResult import io.github.androidpoet.supabase.sync.PushResult -import io.github.androidpoet.supabase.sync.Record import io.github.androidpoet.supabase.sync.RemoteSource +import io.github.androidpoet.supabase.sync.SyncCursor +import io.github.androidpoet.supabase.sync.SyncRecord import kotlinx.coroutines.NonCancellable import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow @@ -45,7 +45,7 @@ public class SupabaseRemoteSource( * — keeps the pull stable when several rows share an `updatedAt`. Returns the new high-water * cursor, or `null` (don't advance) when nothing changed. */ - override suspend fun pull(table: String, since: Cursor?): PullResult { + override suspend fun pull(table: String, since: SyncCursor?): PullResult { val body = database .select( @@ -94,12 +94,12 @@ public class SupabaseRemoteSource( } /** - * A cold [Flow] of [table]'s realtime `postgres_changes`, each emitted as a [Record]. + * A cold [Flow] of [table]'s realtime `postgres_changes`, each emitted as a [SyncRecord]. * INSERT/UPDATE carry the new row; a DELETE is surfaced as a tombstone. Connects the client if * needed and tears the channel down when collection stops. Pair with periodic [pull] to * backfill anything missed while disconnected. */ - override fun changes(table: String): Flow = + override fun changes(table: String): Flow = callbackFlow { if (!realtime.isConnected) realtime.connect() val subscription = diff --git a/supabase-sync/src/commonMain/kotlin/io/github/androidpoet/supabase/sync/remote/SyncColumns.kt b/supabase-sync/src/commonMain/kotlin/io/github/androidpoet/supabase/sync/remote/SyncColumns.kt index 062f49d..29c72d9 100644 --- a/supabase-sync/src/commonMain/kotlin/io/github/androidpoet/supabase/sync/remote/SyncColumns.kt +++ b/supabase-sync/src/commonMain/kotlin/io/github/androidpoet/supabase/sync/remote/SyncColumns.kt @@ -3,7 +3,7 @@ package io.github.androidpoet.supabase.sync.remote /** * Names of the three columns the sync engine needs to find on every synced Supabase table. * - * The engine is transport-neutral: a [io.github.androidpoet.supabase.sync.Record] carries an [id], + * The engine is transport-neutral: a [io.github.androidpoet.supabase.sync.SyncRecord] carries an [id], * a server-set [updatedAt] (epoch millis), and a soft-delete [deleted] tombstone. This maps those * concepts onto your actual Postgres columns so the same table can use whatever naming you already * have. Everything *except* [updatedAt] and [deleted] is treated as a domain field and mirrored diff --git a/supabase-sync/src/commonTest/kotlin/io/github/androidpoet/supabase/sync/remote/MappingTest.kt b/supabase-sync/src/commonTest/kotlin/io/github/androidpoet/supabase/sync/remote/MappingTest.kt index 1ba0a7d..f07482c 100644 --- a/supabase-sync/src/commonTest/kotlin/io/github/androidpoet/supabase/sync/remote/MappingTest.kt +++ b/supabase-sync/src/commonTest/kotlin/io/github/androidpoet/supabase/sync/remote/MappingTest.kt @@ -1,7 +1,7 @@ package io.github.androidpoet.supabase.sync.remote -import io.github.androidpoet.supabase.sync.Cursor -import io.github.androidpoet.supabase.sync.Record +import io.github.androidpoet.supabase.sync.SyncCursor +import io.github.androidpoet.supabase.sync.SyncRecord import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.JsonPrimitive @@ -66,7 +66,7 @@ class MappingTest { record("c", 20), record("b", 20), // same updatedAt, larger id wins on the keyset ) - assertEquals(Cursor(20, "c"), pullCursor(records)) + assertEquals(SyncCursor(20, "c"), pullCursor(records)) } @Test @@ -97,8 +97,8 @@ class MappingTest { assertEquals("hi", obj["title"]?.jsonPrimitive?.content) } - private fun record(id: String, updatedAt: Long, deleted: Boolean = false): Record = - Record( + private fun record(id: String, updatedAt: Long, deleted: Boolean = false): SyncRecord = + SyncRecord( id = id, updatedAt = updatedAt, deleted = deleted, diff --git a/supabase-sync/src/commonTest/kotlin/io/github/androidpoet/supabase/sync/remote/SupabaseRemoteSourceTest.kt b/supabase-sync/src/commonTest/kotlin/io/github/androidpoet/supabase/sync/remote/SupabaseRemoteSourceTest.kt index 82d3c01..2058fcc 100644 --- a/supabase-sync/src/commonTest/kotlin/io/github/androidpoet/supabase/sync/remote/SupabaseRemoteSourceTest.kt +++ b/supabase-sync/src/commonTest/kotlin/io/github/androidpoet/supabase/sync/remote/SupabaseRemoteSourceTest.kt @@ -19,9 +19,9 @@ import io.github.androidpoet.supabase.realtime.RealtimeDebugState import io.github.androidpoet.supabase.realtime.RealtimeSubscription import io.github.androidpoet.supabase.realtime.models.RealtimeChannel import io.github.androidpoet.supabase.sync.ChangeKind -import io.github.androidpoet.supabase.sync.Cursor import io.github.androidpoet.supabase.sync.PendingChange -import io.github.androidpoet.supabase.sync.Record +import io.github.androidpoet.supabase.sync.SyncCursor +import io.github.androidpoet.supabase.sync.SyncRecord import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -65,14 +65,14 @@ class SupabaseRemoteSourceTest { .first() .fields.keys, ) // metadata stripped - assertEquals(Cursor(20, "b"), result.nextCursor) + assertEquals(SyncCursor(20, "b"), result.nextCursor) } @Test fun pull_empty_does_not_advance_cursor() = runTest { val remote = SupabaseRemoteSource(FakeDatabaseClient(selectBody = "[]"), FakeRealtimeClient()) - val result = remote.pull("notes", since = Cursor(5, "a")) + val result = remote.pull("notes", since = SyncCursor(5, "a")) assertTrue(result.changed.isEmpty()) assertEquals(null, result.nextCursor) } @@ -85,7 +85,7 @@ class SupabaseRemoteSourceTest { val change = PendingChange( - Record("a", updatedAt = 7, deleted = false, fields = row("a", "hi")), + SyncRecord("a", updatedAt = 7, deleted = false, fields = row("a", "hi")), ChangeKind.UPSERT, ) val result = remote.push("notes", listOf(change)) @@ -112,7 +112,7 @@ class SupabaseRemoteSourceTest { val change = PendingChange( - Record("a", updatedAt = 9, deleted = true, fields = row("a", "hi")), + SyncRecord("a", updatedAt = 9, deleted = true, fields = row("a", "hi")), ChangeKind.DELETE, ) remote.push("notes", listOf(change)) diff --git a/website/pages/database.mdx b/website/pages/database.mdx index 8e58c57..c9aaecf 100644 --- a/website/pages/database.mdx +++ b/website/pages/database.mdx @@ -178,7 +178,7 @@ For Postgres-specific column types — array columns and full-text search over t ```kotlin where { Users.tags contains listOf("kotlin", "kmp") // also containedBy / overlaps - Users.body.textSearch("kotlin & multiplatform", type = TextSearchType.Websearch) + Users.body.textSearch("kotlin & multiplatform", type = TextSearchType.WEB_SEARCH) } ``` diff --git a/website/pages/index.mdx b/website/pages/index.mdx index f36e806..29be580 100644 --- a/website/pages/index.mdx +++ b/website/pages/index.mdx @@ -42,7 +42,7 @@ Android, iPhone, desktop, the web, and more — the same logic, every platform. - **One codebase, every platform** — share auth, data and realtime logic across mobile, desktop and web. - **Local JWT verification** — `getClaims()` verifies asymmetric tokens on-device against the JWKS, no server round-trip. - **Native sign-in, your way** — optional Google & Apple modules built on a pluggable `NativeAuthProvider`; bring your own for anything else. -- **Built for production** — configurable retry with exponential backoff, a `Network` error category for offline UI, and `suspend` observability hooks for tracing and metrics. +- **Built for production** — configurable retry with exponential backoff, a `NETWORK` error category for offline UI, and `suspend` observability hooks for tracing and metrics. - **Bring-your-own everything** — pluggable session storage, HTTP engine and crypto. Nothing heavy is bundled behind your back. ## Start here diff --git a/website/pages/reference/auth-admin.mdx b/website/pages/reference/auth-admin.mdx index 619a553..6ce63bf 100644 --- a/website/pages/reference/auth-admin.mdx +++ b/website/pages/reference/auth-admin.mdx @@ -653,13 +653,13 @@ non-null. suspend fun listAuditLogEvents( page: Int? = null, perPage: Int? = null, -): SupabaseResult> +): SupabaseResult> ``` - `page` — 1-indexed page number. Default `null` (omitted). - `perPage` — entries per page. Default `null` (omitted). -**Returns** `SupabaseResult>` — `Success` with the entries, `Failure` on error. +**Returns** `SupabaseResult>` — `Success` with the entries, `Failure` on error. ```kotlin val events = admin.listAuditLogEvents(page = 1, perPage = 100) @@ -982,7 +982,7 @@ data class CustomProvider( val skipNonceCheck: Boolean? = null, // skip_nonce_check val authorizationUrl: String? = null, // authorization_url val tokenUrl: String? = null, // token_url - val userinfoUrl: String? = null, // userinfo_url + val userInfoUrl: String? = null, // userinfo_url val jwksUrl: String? = null, // jwks_uri val discoveryDocument: OidcDiscoveryDocument? = null, // discovery_document val createdAt: String? = null, // created_at @@ -1013,7 +1013,7 @@ data class CustomProviderCreateRequest( val skipNonceCheck: Boolean? = null, // skip_nonce_check val authorizationUrl: String? = null, // authorization_url val tokenUrl: String? = null, // token_url - val userinfoUrl: String? = null, // userinfo_url + val userInfoUrl: String? = null, // userinfo_url val jwksUrl: String? = null, // jwks_uri ) ``` @@ -1040,7 +1040,7 @@ data class CustomProviderUpdateRequest( val skipNonceCheck: Boolean? = null, // skip_nonce_check val authorizationUrl: String? = null, // authorization_url val tokenUrl: String? = null, // token_url - val userinfoUrl: String? = null, // userinfo_url + val userInfoUrl: String? = null, // userinfo_url val jwksUrl: String? = null, // jwks_uri ) ``` @@ -1159,13 +1159,13 @@ data class Passkey( ) ``` -### AuditLogEntry +### AuditLogEvent A single audit-log entry from `listAuditLogEvents`. The `payload` shape depends on the recorded action, so it is kept as a raw `JsonObject` rather than a typed model. ```kotlin -data class AuditLogEntry( +data class AuditLogEvent( val id: String, val payload: JsonObject? = null, val createdAt: String? = null, // created_at diff --git a/website/pages/reference/core.mdx b/website/pages/reference/core.mdx index f575101..4200fea 100644 --- a/website/pages/reference/core.mdx +++ b/website/pages/reference/core.mdx @@ -105,7 +105,7 @@ public inline fun SupabaseResult.flatMapError(transform: (SupabaseError) ```kotlin val profile = api.fetchProfile() - .flatMapError { err -> if (err.category == SupabaseErrorCategory.Unauthorized) refreshAndRetry() else SupabaseResult.Failure(err) } + .flatMapError { err -> if (err.category == SupabaseErrorCategory.UNAUTHORIZED) refreshAndRetry() else SupabaseResult.Failure(err) } .recover { Profile.GUEST } // never fails after this ``` @@ -297,7 +297,7 @@ The throwable form of a `SupabaseError`, thrown by `getOrThrow()` / `dataFlow` a ```kotlin public enum class SupabaseErrorCategory { - Conflict, NotFound, Unauthorized, RateLimited, Validation, Internal, Network, Unknown + CONFLICT, NOT_FOUND, UNAUTHORIZED, RATE_LIMITED, VALIDATION, INTERNAL, NETWORK, UNKNOWN } public val SupabaseError.category: SupabaseErrorCategory @@ -306,18 +306,18 @@ public val SupabaseErrorCategory.isRetryable: Boolean A coarse classification of a failure so callers can branch on the **kind** of error without matching individual codes. -- **`Conflict`** — uniqueness/foreign-key clash or already-existing resource (HTTP 409). -- **`NotFound`** — the table, row, user or object does not exist (HTTP 404). -- **`Unauthorized`** — missing/invalid/insufficient credentials or permissions (HTTP 401/403). -- **`RateLimited`** — a rate limit was exceeded; back off and retry (HTTP 429). -- **`Validation`** — the request was malformed or failed validation (HTTP 400/406/416/422). -- **`Internal`** — a server-side failure unrelated to request content (HTTP 5xx, plus transient 408/425). -- **`Network`** — the request never reached the server or never produced a usable response (offline, timeout, connection or decode failure). Distinct from `Unknown` so you can show offline/retry UI. -- **`Unknown`** — the catch-all fallback when nothing more specific applied. +- **`CONFLICT`** — uniqueness/foreign-key clash or already-existing resource (HTTP 409). +- **`NOT_FOUND`** — the table, row, user or object does not exist (HTTP 404). +- **`UNAUTHORIZED`** — missing/invalid/insufficient credentials or permissions (HTTP 401/403). +- **`RATE_LIMITED`** — a rate limit was exceeded; back off and retry (HTTP 429). +- **`VALIDATION`** — the request was malformed or failed validation (HTTP 400/406/416/422). +- **`INTERNAL`** — a server-side failure unrelated to request content (HTTP 5xx, plus transient 408/425). +- **`NETWORK`** — the request never reached the server or never produced a usable response (offline, timeout, connection or decode failure). Distinct from `UNKNOWN` so you can show offline/retry UI. +- **`UNKNOWN`** — the catch-all fallback when nothing more specific applied. **`category`** resolves from the textual `code` first (matched against the per-service code sets in [`SupabaseErrorCodes`](#supabaseerrorcodes)), then falls back to `httpStatus` (or a numeric `code` in the HTTP range), so structured bodies without a textual code are still classified. -**`isRetryable`** is `true` for the transient categories — `RateLimited`, `Internal`, `Network` — and `false` for client-fault categories that would fail again unchanged. +**`isRetryable`** is `true` for the transient categories — `RATE_LIMITED`, `INTERNAL`, `NETWORK` — and `false` for client-fault categories that would fail again unchanged. ```kotlin result.onFailure { err -> @@ -360,7 +360,7 @@ A catalog of the string error codes Supabase services return, grouped by service - **`Storage`** — object/bucket and S3-compatibility codes: `NO_SUCH_BUCKET`, `NO_SUCH_KEY`, `KEY_ALREADY_EXISTS`, `ENTITY_TOO_LARGE`, `INVALID_MIME_TYPE`, `ACCESS_DENIED`, `THROTTLING`, `INTERNAL_ERROR`, and more. - **`Realtime`** — WebSocket codes: `CHANNEL_RATE_LIMIT_REACHED`, `CONNECTION_RATE_LIMIT_REACHED`, `ERROR_AUTHORIZING_WEBSOCKET`, `AUTH_EXPIRED`, `INVALID_TOPIC`, `PAYLOAD_TOO_LARGE`, and others. - **`Functions`** — Edge Functions codes: `BOOT_ERROR`, `WORKER_ERROR`, `WORKER_LIMIT`, `DEPLOYMENT_FAILED`, `UNSUPPORTED_NODE_VERSION`, `FUNCTION_NOT_RETURNING_RESPONSE`. -- **`Client`** — the only group **not** emitted by a server; synthesized by the SDK when a request never produced a response: `NETWORK_ERROR`, `TIMEOUT`, `CONNECTION_FAILED`. These let `category` resolve to `Network` instead of `Unknown`. +- **`Client`** — the only group **not** emitted by a server; synthesized by the SDK when a request never produced a response: `NETWORK_ERROR`, `TIMEOUT`, `CONNECTION_FAILED`. These let `category` resolve to `NETWORK` instead of `UNKNOWN`. - **`Management`** — Management API codes: `PROJECT_NOT_FOUND`, `ORGANIZATION_NOT_FOUND`, `INVALID_PROJECT_REF`, `UNAUTHORIZED`, `FORBIDDEN`, `RATE_LIMIT_EXCEEDED`. ```kotlin @@ -508,12 +508,12 @@ public infix fun Column<*>.rangeAdjacent(range: String) // -|- adjacent to **Full-text search:** ```kotlin -public fun Column.textSearch(query: String, config: String? = null, type: TextSearchType = TextSearchType.Plain) +public fun Column.textSearch(query: String, config: String? = null, type: TextSearchType = TextSearchType.PLAIN) ``` - `query` — the search string. - `config` — an optional text-search configuration (e.g. `"english"`), wrapped in parentheses on the wire. Default `null`. -- `type` — the parse mode; default `TextSearchType.Plain`. +- `type` — the parse mode; default `TextSearchType.PLAIN`. **Logical grouping and escape hatch:** diff --git a/website/pages/reference/database.mdx b/website/pages/reference/database.mdx index 49462e9..3930dbf 100644 --- a/website/pages/reference/database.mdx +++ b/website/pages/reference/database.mdx @@ -180,7 +180,7 @@ public suspend inline fun DatabaseClient.selectSingleTyped( Reads the single row matching the filters and decodes it into `T`. Requests `format = SINGLE`, so PostgREST returns HTTP 406 (a `Failure` with -`SupabaseErrorCategory.NotFound`) when zero or more than one row matches. +`SupabaseErrorCategory.NOT_FOUND`) when zero or more than one row matches. - Parameters as for [`selectTyped`](#selecttyped). @@ -202,7 +202,7 @@ public suspend inline fun DatabaseClient.selectMaybeSingleTyped( ``` The lenient variant of `selectSingleTyped`: reads at most one row, mapping a "no -rows" response (HTTP 406 / `NotFound`) to `Success(null)` instead of a failure. +rows" response (HTTP 406 / `NOT_FOUND`) to `Success(null)` instead of a failure. Still fails if more than one row matches. - Parameters as for [`selectTyped`](#selecttyped). @@ -966,7 +966,7 @@ public suspend inline fun DatabaseClie ``` Calls a procedure expecting exactly one row/scalar, decoded into `T`. Requests -`format = SINGLE`, so zero or more than one row fails with HTTP 406 / `NotFound`. +`format = SINGLE`, so zero or more than one row fails with HTTP 406 / `NOT_FOUND`. - Parameters as on [`rpcTyped`](#rpctyped). @@ -989,7 +989,7 @@ public suspend inline fun DatabaseClie ``` The lenient variant of `rpcSingleTyped`: a "no rows" response (HTTP 406 / -`NotFound`) maps to `Success(null)`; other failures propagate. +`NOT_FOUND`) maps to `Success(null)`; other failures propagate. - Parameters as on [`rpcTyped`](#rpctyped). @@ -1201,7 +1201,7 @@ public suspend inline fun DatabaseClie ): SupabaseResult ``` -The GET counterpart of `rpcSingleTyped`. Fails with HTTP 406 / `NotFound` when not +The GET counterpart of `rpcSingleTyped`. Fails with HTTP 406 / `NOT_FOUND` when not exactly one row is returned. - Parameters as on [`rpcGetTyped`](#rpcgettyped). @@ -1225,7 +1225,7 @@ public suspend inline fun DatabaseClie ): SupabaseResult ``` -The GET, lenient counterpart of `rpcSingleTyped`: "no rows" (HTTP 406 / `NotFound`) +The GET, lenient counterpart of `rpcSingleTyped`: "no rows" (HTTP 406 / `NOT_FOUND`) maps to `Success(null)`. - Parameters as on [`rpcGetTyped`](#rpcgettyped). @@ -1360,7 +1360,7 @@ escaped per PostgREST's quoting rules; numbers and booleans are emitted verbatim **Range columns:** `rangeGt` (`sr`), `rangeGte` (`nxl`), `rangeLt` (`sl`), `rangeLte` (`nxr`), `rangeAdjacent` (`adj`) — each takes a range literal `String`. -**Full-text search:** `textSearch(query: String, config: String? = null, type: TextSearchType = TextSearchType.Plain)`. +**Full-text search:** `textSearch(query: String, config: String? = null, type: TextSearchType = TextSearchType.PLAIN)`. **Escape hatch:** `raw(column: Column<*>, operator: String, value: String)` — emits `column=.` for operators the DSL doesn't expose yet. diff --git a/website/pages/reference/realtime.mdx b/website/pages/reference/realtime.mdx index 005cb6c..a9a9632 100644 --- a/website/pages/reference/realtime.mdx +++ b/website/pages/reference/realtime.mdx @@ -289,7 +289,7 @@ val sub = realtime.channel("room1") ```kotlin suspend fun RealtimeClient.subscribe( - channel: String, + channelName: String, configure: RealtimeChannelBuilder.() -> Unit = {}, ): RealtimeSubscription ``` @@ -601,10 +601,10 @@ A live subscription to one joined channel: the handle returned by `subscribe()` for observing events and sending on the channel. It stays valid across reconnects — the client rejoins automatically. -#### `channel` +#### `channelName` ```kotlin -val channel: String +val channelName: String ``` The channel name (the part after `realtime:`). @@ -805,7 +805,7 @@ the common "watch this table" cases. ```kotlin suspend fun RealtimeClient.subscribeToPostgresChanges( - channel: String, + channelName: String, schema: String = "public", table: String? = null, filter: String? = null, @@ -814,7 +814,7 @@ suspend fun RealtimeClient.subscribeToPostgresChanges( ): RealtimeSubscription suspend fun RealtimeClient.subscribeToPostgresChanges( - channel: String, + channelName: String, schema: String = "public", table: String? = null, filter: String? = null, @@ -823,7 +823,7 @@ suspend fun RealtimeClient.subscribeToPostgresChanges( ): RealtimeSubscription suspend inline fun RealtimeClient.subscribeToPostgresChanges( - channel: String, + channelName: String, schema: String = "public", table: String? = null, filter: String? = null, @@ -832,7 +832,7 @@ suspend inline fun RealtimeClient.subscribeToPostgresChanges( ): RealtimeSubscription ``` -Subscribes to `channel` and registers a single `postgres_changes` callback in one +Subscribes to `channelName` and registers a single `postgres_changes` callback in one call. The three overloads deliver, respectively: the raw row (`JsonObject`); the resolved change type plus the row; and the row decoded into your model `T` (with the same skip-on-mismatch semantics as the reified `onPostgresChange`). See @@ -841,7 +841,7 @@ for building `filter`. ```kotlin val sub = realtime.subscribeToPostgresChanges( - channel = "rooms", + channelName = "rooms", table = "messages", filter = realtimeFilter { eq("room_id", roomId) }, ) { msg -> store(msg) } @@ -975,7 +975,7 @@ the helpers here cover HTTP send, one-call subscribe, and consuming flows. ```kotlin suspend fun RealtimeClient.broadcast( - channel: String, + channelName: String, event: String, payload: JsonObject, private: Boolean = false, @@ -987,7 +987,7 @@ Sends a broadcast over HTTP to the realtime broadcast endpoint Useful for fire-and-forget server-to-client fan-out where the sender doesn't need to subscribe. -- `channel` — the channel name (without the `realtime:` prefix). +- `channelName` — the channel name (without the `realtime:` prefix). - `event` — the broadcast event name. - `payload` — the message body. - `private` — when `true`, targets a private channel; the caller's session JWT @@ -1003,13 +1003,13 @@ realtime.broadcast("room1", "notice", buildJsonObject { put("text", "hi") }) ```kotlin suspend fun RealtimeClient.subscribeToBroadcast( - channel: String, + channelName: String, event: String, callback: suspend (JsonObject) -> Unit, ): RealtimeSubscription ``` -Subscribes to `channel` and registers a single broadcast `callback` for the named +Subscribes to `channelName` and registers a single broadcast `callback` for the named `event` in one call. ### `broadcastFlow` @@ -1060,12 +1060,12 @@ subscribe and consuming flows. ```kotlin suspend fun RealtimeClient.subscribeToPresence( - channel: String, + channelName: String, callback: suspend (PresenceState) -> Unit, ): RealtimeSubscription ``` -Subscribes to `channel` and registers a single presence sync `callback`, invoked +Subscribes to `channelName` and registers a single presence sync `callback`, invoked with the full [`PresenceState`](#presencestate). ### `presenceSyncFlow` diff --git a/website/pages/reference/storage.mdx b/website/pages/reference/storage.mdx index 01eb046..22d8fa1 100644 --- a/website/pages/reference/storage.mdx +++ b/website/pages/reference/storage.mdx @@ -571,7 +571,7 @@ val entries = storage.list(bucket = "avatars", prefix = "jane") ### listV2 Cursor-paged object listing (`POST /object/list-v2`) returning an -[`ObjectListV2Result`](#objectlistv2result) that separates folders from objects +[`ObjectListV2Response`](#objectlistv2result) that separates folders from objects and carries a `nextCursor`/`hasNext` for continuation. Prefer this over [`list`](#list) for large buckets or delimiter-style folder navigation. @@ -584,7 +584,7 @@ suspend fun listV2( withDelimiter: Boolean? = null, sortBy: String? = null, sortOrder: SortOrder = SortOrder.ASC, -): SupabaseResult +): SupabaseResult ``` - `bucket` — bucket to list. @@ -595,7 +595,7 @@ suspend fun listV2( - `sortBy` — column to sort by; `null` leaves the server default. Default `null`. - `sortOrder` — order applied when `sortBy` is set. Default `SortOrder.ASC`. -**Returns** `SupabaseResult` — `Success` with the page, `Failure` on error. +**Returns** `SupabaseResult` — `Success` with the page, `Failure` on error. ```kotlin var page = storage.listV2(bucket = "avatars", withDelimiter = true).getOrThrow() @@ -1987,13 +1987,13 @@ data class FileObject( - `etag` — the entity tag, when reported. - `cacheControl` — the stored cache-control directive, when reported. -### ObjectListV2Result +### ObjectListV2Response Result of a cursor-paged v2 listing ([`listV2`](#listv2)), separating folders from objects and carrying continuation state. ```kotlin -data class ObjectListV2Result( +data class ObjectListV2Response( val hasNext: Boolean, val folders: List, val objects: List, diff --git a/website/pages/results-and-errors.mdx b/website/pages/results-and-errors.mdx index 715bd03..19a0605 100644 --- a/website/pages/results-and-errors.mdx +++ b/website/pages/results-and-errors.mdx @@ -118,7 +118,7 @@ public data class SupabaseError( `httpStatus` is always captured when a response was received — so a `404` or a GoTrue error body without a machine code still categorizes correctly instead of -collapsing to `Unknown`. +collapsing to `UNKNOWN`. ### Categories @@ -126,18 +126,18 @@ Every `SupabaseError` maps to a `category` for branching without parsing codes: ```kotlin when (result.errorOrNull()?.category) { - SupabaseErrorCategory.Conflict -> /* 409, unique/constraint */ - SupabaseErrorCategory.NotFound -> /* 404 */ - SupabaseErrorCategory.Unauthorized -> /* 401 / 403 */ - SupabaseErrorCategory.RateLimited -> /* 429 */ - SupabaseErrorCategory.Validation -> /* 400 / 422 */ - SupabaseErrorCategory.Internal -> /* 5xx */ - SupabaseErrorCategory.Network -> /* offline, DNS/TLS, timeout — no response */ - SupabaseErrorCategory.Unknown, null -> /* fallback */ + SupabaseErrorCategory.CONFLICT -> /* 409, unique/constraint */ + SupabaseErrorCategory.NOT_FOUND -> /* 404 */ + SupabaseErrorCategory.UNAUTHORIZED -> /* 401 / 403 */ + SupabaseErrorCategory.RATE_LIMITED -> /* 429 */ + SupabaseErrorCategory.VALIDATION -> /* 400 / 422 */ + SupabaseErrorCategory.INTERNAL -> /* 5xx */ + SupabaseErrorCategory.NETWORK -> /* offline, DNS/TLS, timeout — no response */ + SupabaseErrorCategory.UNKNOWN, null -> /* fallback */ } ``` -`Network` is distinct from `Unknown` so you can show offline/retry UI when no +`NETWORK` is distinct from `UNKNOWN` so you can show offline/retry UI when no response was received at all. Two helpers make the common branches terse: ```kotlin diff --git a/website/pages/spring-boot.mdx b/website/pages/spring-boot.mdx index 142ca51..d67a26b 100644 --- a/website/pages/spring-boot.mdx +++ b/website/pages/spring-boot.mdx @@ -116,11 +116,11 @@ fun SupabaseResult.unwrap(): T = is SupabaseResult.Success -> value is SupabaseResult.Failure -> { val status = when (error.category) { - SupabaseErrorCategory.Conflict -> HttpStatus.CONFLICT - SupabaseErrorCategory.NotFound -> HttpStatus.NOT_FOUND - SupabaseErrorCategory.Unauthorized -> HttpStatus.UNAUTHORIZED - SupabaseErrorCategory.RateLimited -> HttpStatus.TOO_MANY_REQUESTS - SupabaseErrorCategory.Validation -> HttpStatus.BAD_REQUEST + SupabaseErrorCategory.CONFLICT -> HttpStatus.CONFLICT + SupabaseErrorCategory.NOT_FOUND -> HttpStatus.NOT_FOUND + SupabaseErrorCategory.UNAUTHORIZED -> HttpStatus.UNAUTHORIZED + SupabaseErrorCategory.RATE_LIMITED -> HttpStatus.TOO_MANY_REQUESTS + SupabaseErrorCategory.VALIDATION -> HttpStatus.BAD_REQUEST else -> HttpStatus.BAD_GATEWAY } throw ResponseStatusException(status, error.message)