Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
29471d1
added channel helpers for realtime in the client sdks
ArnabChatterjee20k Dec 18, 2025
3f13cb1
Add Channel support to SDKs and templates
ArnabChatterjee20k Dec 18, 2025
5d19169
reverted changes
ArnabChatterjee20k Dec 18, 2025
b18671f
Refactor Channel references to use NIOCore.Channel and remove unused …
ArnabChatterjee20k Dec 18, 2025
fd57b65
Refractor channel to use builder pattern
ArnabChatterjee20k Dec 19, 2025
3e2402d
updated flutter config
ArnabChatterjee20k Dec 19, 2025
4ff8dd6
Refactor channel handling and subscription logic across multiple temp…
ArnabChatterjee20k Dec 30, 2025
61710ea
Refactor channel and interface definitions for improved clarity and c…
ArnabChatterjee20k Dec 30, 2025
9252cc3
fixed flutter
ArnabChatterjee20k Dec 30, 2025
4c3bef7
updated kotlin and swift
ArnabChatterjee20k Dec 30, 2025
05046c8
fixed swift
ArnabChatterjee20k Dec 30, 2025
9f5f635
Refactor channel method names for consistency across languages
ArnabChatterjee20k Jan 9, 2026
0b840f9
updated bucket
ArnabChatterjee20k Jan 9, 2026
7571e2d
updated react native and web ts
ArnabChatterjee20k Jan 9, 2026
2ed26c9
updated channel and resolved channel validation in web
ArnabChatterjee20k Jan 9, 2026
77147bd
added overloading for type strictness in kotlin
ArnabChatterjee20k Jan 9, 2026
6b30e48
removed leftovers
ArnabChatterjee20k Jan 9, 2026
cd88974
updated leftover
ArnabChatterjee20k Jan 9, 2026
d248155
fixed failing swift tests
ArnabChatterjee20k Jan 9, 2026
b0533b1
fixed failing swift tests
ArnabChatterjee20k Jan 9, 2026
903bdb3
Merge remote-tracking branch 'origin/dat-971' into dat-971
ArnabChatterjee20k Jan 9, 2026
18152a1
revamped realtime channel support for the js realtime
ArnabChatterjee20k Jan 12, 2026
9a74e33
refactor: enhance channel structure and type safety across multiple l…
ArnabChatterjee20k Jan 12, 2026
6d627f3
Merge remote-tracking branch 'origin/master' into dat-971
ArnabChatterjee20k Jan 12, 2026
dde2c4b
refactor: update channel interfaces for improved type safety
ArnabChatterjee20k Jan 12, 2026
3ed2d3e
updated kotlin
ArnabChatterjee20k Jan 12, 2026
163e7fc
updated swift issue
ArnabChatterjee20k Jan 12, 2026
45a8b61
Merge remote-tracking branch 'origin/master' into dat-971
ArnabChatterjee20k Jan 13, 2026
8af59f4
fixed android client build
ArnabChatterjee20k Jan 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/SDK/Language/Android.php
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,11 @@ public function getFiles(): array
'destination' => '/library/src/main/java/{{ sdk.namespace | caseSlash }}/ID.kt',
'template' => '/android/library/src/main/java/io/package/ID.kt.twig',
],
[
'scope' => 'default',
'destination' => '/library/src/main/java/{{ sdk.namespace | caseSlash }}/Channel.kt',
'template' => '/android/library/src/main/java/io/package/Channel.kt.twig',
],
[
'scope' => 'default',
'destination' => '/library/src/main/java/{{ sdk.namespace | caseSlash }}/Query.kt',
Expand Down
5 changes: 5 additions & 0 deletions src/SDK/Language/Apple.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ public function getFiles(): array
'destination' => '/Sources/{{ spec.title | caseUcfirst}}/ID.swift',
'template' => 'swift/Sources/ID.swift.twig',
],
[
'scope' => 'default',
'destination' => '/Sources/{{ spec.title | caseUcfirst}}/Channel.swift',
'template' => 'apple/Sources/Channel.swift.twig',
],
[
'scope' => 'default',
'destination' => '/Sources/{{ spec.title | caseUcfirst}}/Query.swift',
Expand Down
10 changes: 10 additions & 0 deletions src/SDK/Language/Flutter.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@ public function getFiles(): array
'destination' => '/lib/id.dart',
'template' => 'dart/lib/id.dart.twig',
],
[
'scope' => 'default',
'destination' => '/lib/channel.dart',
'template' => 'flutter/lib/channel.dart.twig',
],
[
'scope' => 'default',
'destination' => '/lib/query.dart',
Expand Down Expand Up @@ -295,6 +300,11 @@ public function getFiles(): array
'destination' => '/test/role_test.dart',
'template' => 'dart/test/role_test.dart.twig',
],
[
'scope' => 'default',
'destination' => '/test/channel_test.dart',
'template' => 'flutter/test/src/channel_test.dart.twig',
],
[
'scope' => 'default',
'destination' => '/test/src/cookie_manager_test.dart',
Expand Down
5 changes: 5 additions & 0 deletions src/SDK/Language/ReactNative.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ public function getFiles(): array
'destination' => 'src/id.ts',
'template' => 'react-native/src/id.ts.twig',
],
[
'scope' => 'default',
'destination' => 'src/channel.ts',
'template' => 'react-native/src/channel.ts.twig',
],
[
'scope' => 'default',
'destination' => 'src/query.ts',
Expand Down
5 changes: 5 additions & 0 deletions src/SDK/Language/Web.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@ public function getFiles(): array
'destination' => 'src/id.ts',
'template' => 'web/src/id.ts.twig',
],
[
'scope' => 'default',
'destination' => 'src/channel.ts',
'template' => 'web/src/channel.ts.twig',
],
[
'scope' => 'default',
'destination' => 'src/query.ts',
Expand Down
261 changes: 261 additions & 0 deletions templates/android/library/src/main/java/io/package/Channel.kt.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
package {{ sdk.namespace | caseDot }}

// Marker interfaces for type safety
public interface _Root
public interface _Database
public interface _Collection
public interface _Document
public interface _TablesDB
public interface _Table
public interface _Row
public interface _Bucket
public interface _File
public interface _Func
public interface _Execution
public interface _Team
public interface _Membership
public interface _Resolved

// Union type for actionable channels
typealias Actionable = _Document

Comment thread
coderabbitai[bot] marked this conversation as resolved.
/**
* Helper for normalizing IDs
*/
private fun normalize(id: String): String {
val trimmed = id.trim()
return if (trimmed.isEmpty()) "*" else trimmed
}

/**
* Channel class with generic type parameter for type-safe method chaining
*/
class Channel<out T> private constructor(
private val segments: List<String>
) {
/**
* Internal helper to transition to next state with segment and ID
* Public for extension function access
*/
fun <N> next(segment: String, id: String = "*"): Channel<N> {
return Channel(segments + listOf(segment, normalize(id)))
}

/**
* Internal helper for terminal actions (no ID segment)
* Public for extension function access
*/
fun resolve(action: String): Channel<_Resolved> {
return Channel(segments + listOf(action))
}

/**
* Convert channel to string representation
*/
override fun toString(): String = segments.joinToString(".")

companion object {
fun database(id: String = "*"): Channel<_Database> =
Channel(listOf("databases", normalize(id)))

fun tablesdb(id: String = "*"): Channel<_TablesDB> =
Channel(listOf("tablesdb", normalize(id)))

fun bucket(id: String = "*"): Channel<_Bucket> =
Channel(listOf("buckets", normalize(id)))

fun function(id: String = "*"): Channel<_Func> =
Channel(listOf("functions", normalize(id)))

fun team(id: String = "*"): Channel<_Team> =
Channel(listOf("teams", normalize(id)))

fun membership(id: String = "*"): Channel<_Membership> =
Channel(listOf("memberships", normalize(id)))

fun account(userId: String = ""): String {
val id = normalize(userId)
return if (id == "*") "account" else "account.$id"
}

// Global events
const val documents = "documents"
const val rows = "rows"
const val files = "files"
const val executions = "executions"
}
}

// --- DATABASE ROUTE ---
// Extension functions restricted by receiver type

/**
* Only available on Channel<_Database>
*/
fun Channel<_Database>.collection(id: String = "*"): Channel<_Collection> =
this.next("collections", id)

/**
* Only available on Channel<_Collection>
*/
fun Channel<_Collection>.document(id: String = "*"): Channel<_Document> =
this.next("documents", id)

// --- TABLESDB ROUTE ---

/**
* Only available on Channel<_TablesDB>
*/
fun Channel<_TablesDB>.table(id: String = "*"): Channel<_Table> =
this.next("tables", id)

/**
* Only available on Channel<_Table>
*/
fun Channel<_Table>.row(id: String = "*"): Channel<_Row> =
this.next("rows", id)

// --- BUCKET ROUTE ---

/**
* Only available on Channel<_Bucket>
*/
fun Channel<_Bucket>.file(id: String = "*"): Channel<_File> =
this.next("files", id)

// --- FUNCTION ROUTE ---

/**
* Only available on Channel<_Func>
*/
fun Channel<_Func>.execution(id: String = "*"): Channel<_Execution> =
this.next("executions", id)

// --- TERMINAL ACTIONS ---
// Restricted to Actionable types (_Document, _Row, _File, _Execution, _Team, _Membership)

/**
* Only available on Channel<_Document>
*/
@JvmName("createDocument")
fun Channel<_Document>.create(): Channel<_Resolved> =
this.resolve("create")

/**
* Only available on Channel<_Document>
*/
@JvmName("updateDocument")
fun Channel<_Document>.update(): Channel<_Resolved> =
this.resolve("update")

/**
* Only available on Channel<_Document>
*/
@JvmName("deleteDocument")
fun Channel<_Document>.delete(): Channel<_Resolved> =
this.resolve("delete")

/**
* Only available on Channel<_Row>
*/
@JvmName("createRow")
fun Channel<_Row>.create(): Channel<_Resolved> =
this.resolve("create")

/**
* Only available on Channel<_Row>
*/
@JvmName("updateRow")
fun Channel<_Row>.update(): Channel<_Resolved> =
this.resolve("update")

/**
* Only available on Channel<_Row>
*/
@JvmName("deleteRow")
fun Channel<_Row>.delete(): Channel<_Resolved> =
this.resolve("delete")

/**
* Only available on Channel<_File>
*/
@JvmName("createFile")
fun Channel<_File>.create(): Channel<_Resolved> =
this.resolve("create")

/**
* Only available on Channel<_File>
*/
@JvmName("updateFile")
fun Channel<_File>.update(): Channel<_Resolved> =
this.resolve("update")

/**
* Only available on Channel<_File>
*/
@JvmName("deleteFile")
fun Channel<_File>.delete(): Channel<_Resolved> =
this.resolve("delete")

/**
* Only available on Channel<_Execution>
*/
@JvmName("createExecution")
fun Channel<_Execution>.create(): Channel<_Resolved> =
this.resolve("create")

/**
* Only available on Channel<_Execution>
*/
@JvmName("updateExecution")
fun Channel<_Execution>.update(): Channel<_Resolved> =
this.resolve("update")

/**
* Only available on Channel<_Execution>
*/
@JvmName("deleteExecution")
fun Channel<_Execution>.delete(): Channel<_Resolved> =
this.resolve("delete")

/**
* Only available on Channel<_Team>
*/
@JvmName("createTeam")
fun Channel<_Team>.create(): Channel<_Resolved> =
this.resolve("create")

/**
* Only available on Channel<_Team>
*/
@JvmName("updateTeam")
fun Channel<_Team>.update(): Channel<_Resolved> =
this.resolve("update")

/**
* Only available on Channel<_Team>
*/
@JvmName("deleteTeam")
fun Channel<_Team>.delete(): Channel<_Resolved> =
this.resolve("delete")

/**
* Only available on Channel<_Membership>
*/
@JvmName("createMembership")
fun Channel<_Membership>.create(): Channel<_Resolved> =
this.resolve("create")

/**
* Only available on Channel<_Membership>
*/
@JvmName("updateMembership")
fun Channel<_Membership>.update(): Channel<_Resolved> =
this.resolve("update")

/**
* Only available on Channel<_Membership>
*/
@JvmName("deleteMembership")
fun Channel<_Membership>.delete(): Channel<_Resolved> =
this.resolve("delete")
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package {{ sdk.namespace | caseDot }}.services

import {{ sdk.namespace | caseDot }}.Service
import {{ sdk.namespace | caseDot }}.Client
import {{ sdk.namespace | caseDot }}.Channel
import {{ sdk.namespace | caseDot }}.exceptions.{{ spec.title | caseUcfirst }}Exception
import {{ sdk.namespace | caseDot }}.extensions.forEachAsync
import {{ sdk.namespace | caseDot }}.extensions.fromJson
Expand Down Expand Up @@ -109,6 +110,27 @@ class Realtime(client: Client) : Service(client), CoroutineScope {
else -> 60000L
}

/**
* Convert channel value to string
* All Channel instances have toString() method
*/
private fun channelToString(channel: Any): String {
return when {
channel is String -> channel
channel is Channel<*> -> channel.toString()
else -> channel.toString()
}
}

fun subscribe(
vararg channels: Channel<*>,
callback: (RealtimeResponseEvent<Any>) -> Unit,
) = subscribe(
channels = channels.map { channelToString(it) }.toTypedArray(),
Any::class.java,
callback
)

fun subscribe(
vararg channels: String,
callback: (RealtimeResponseEvent<Any>) -> Unit,
Expand All @@ -118,6 +140,18 @@ class Realtime(client: Client) : Service(client), CoroutineScope {
callback
)

fun <T> subscribe(
vararg channels: Channel<*>,
payloadType: Class<T>,
callback: (RealtimeResponseEvent<T>) -> Unit,
): RealtimeSubscription {
return subscribe(
channels = channels.map { channelToString(it) }.toTypedArray(),
payloadType = payloadType,
callback = callback
)
}

fun <T> subscribe(
vararg channels: String,
payloadType: Class<T>,
Expand Down
Loading
Loading