1+ import dev.datastar.kotlin.sdk.Response
2+ import dev.datastar.kotlin.sdk.ServerSentEventGenerator
3+ import io.ktor.http.ContentType
4+ import io.ktor.http.HttpStatusCode.Companion.NoContent
5+ import io.ktor.http.HttpStatusCode.Companion.OK
6+ import io.ktor.server.engine.embeddedServer
7+ import io.ktor.server.netty.Netty
8+ import io.ktor.server.response.respondBytes
9+ import io.ktor.server.response.respondTextWriter
10+ import io.ktor.server.routing.get
11+ import io.ktor.server.routing.post
12+ import io.ktor.server.routing.routing
13+ import kotlinx.coroutines.flow.MutableStateFlow
14+ import java.io.File
15+ import java.io.Writer
16+
17+ // /usr/bin/env jbang "$0" "$@" ; exit $?
18+ // JAVA 21
19+ // KOTLIN 2.2.0
20+ // DEPS dev.cloudgt.datastar:kotlin-sdk:0.1.0
21+ // DEPS io.ktor:ktor-server-core-jvm:3.2.3
22+ // DEPS io.ktor:ktor-server-netty-jvm:3.2.3
23+
24+
25+ fun main () {
26+ server().run {
27+ println (" Let's go counting star... http://localhost:8080" )
28+ start(wait = true )
29+ }
30+ }
31+
32+ fun server (
33+ counter : MutableStateFlow <Int > = MutableStateFlow (0),
34+ ) = embeddedServer(Netty , port = 8080 ) {
35+
36+ val counterPage = File (
37+ " ../front/counter.html"
38+ ).readBytes()
39+
40+ routing {
41+
42+ get(" /" ) {
43+ call.response.headers.append(" Content-Type" , " text/html" )
44+ call.response.status(OK )
45+ call.respondBytes(counterPage)
46+ }
47+
48+ get(" /counter" ) {
49+ call.respondTextWriter(
50+ status = OK ,
51+ contentType = ContentType .Text .EventStream ,
52+ ) {
53+
54+ val response = adaptResponse(this )
55+ val generator = ServerSentEventGenerator (response)
56+
57+ counter.collect { event ->
58+ generator.patchElements(""" <span id="counter">${event} </span>""" )
59+
60+ if (event == 3 ) {
61+ generator.executeScript(""" alert('Thanks for trying Datastar!')""" )
62+ }
63+ }
64+
65+ }
66+ }
67+
68+ post(" /increment" ) {
69+ counter.value++
70+ call.response.status(NoContent )
71+ }
72+
73+ post(" /decrement" ) {
74+ counter.value--
75+ call.response.status(NoContent )
76+ }
77+
78+ }
79+ }
80+
81+ private fun adaptResponse (writer : Writer ): Response =
82+ object : Response {
83+ override fun sendConnectionHeaders (
84+ status : Int ,
85+ headers : Map <String , List <String >>,
86+ ) {
87+ // connection is already set up when used
88+ }
89+
90+ override fun write (text : String ) {
91+ writer.write(text)
92+ }
93+
94+ override fun flush () {
95+ writer.flush()
96+ }
97+ }
0 commit comments