@arrirpc/codegen-kotlin 0.60.3 → 0.61.0

Sign up to get free protection for your applications and to get access to all the features.
package/README.md CHANGED
@@ -24,7 +24,7 @@ export default defineConfig({
24
24
  | --------------------- | ------------------------------------------------------------- |
25
25
  | clientName | The name of the generated client class (Defaults to "Client") |
26
26
  | outputFile (required) | Path to the file that will be created by the generator |
27
- | modelPrefix | Add a prefix to the generated class names |
27
+ | typePrefix | Add a prefix to the generated class names |
28
28
 
29
29
  ### 2) Install dependencies
30
30
 
@@ -72,6 +72,77 @@ val service = MyClientUsersService(
72
72
  )
73
73
  ```
74
74
 
75
+ ### Calling Procedures
76
+
77
+ #### Standard HTTP Procedures
78
+
79
+ ```kotlin
80
+ runBlocking {
81
+ // procedure with no parameters
82
+ val getUsersResponse = myClient.users.getUsers()
83
+
84
+ // procedure with parameters
85
+ val getUserResponse = myClient.users.getUser(GetUserParams(userId = "12345"))
86
+ }
87
+ ```
88
+
89
+ #### Event Stream Procedures
90
+
91
+ ##### Basic Usage
92
+
93
+ ```kotlin
94
+ runBlocking {
95
+ myClient.users.watchUserChanges(
96
+ onData { message ->
97
+ println("New message: ${message}")
98
+ },
99
+ onOpen {
100
+ println("Connection established")
101
+ }
102
+ onRequestError { err ->
103
+ println("Error connecting to server: ${err}")
104
+ },
105
+ onResponseError { err ->
106
+ println("Server returned an error: ${err.code} ${err.message}")
107
+ },
108
+ onClose {
109
+ println("Connection closed")
110
+ }
111
+ )
112
+ }
113
+ ```
114
+
115
+ ##### Cancelling Requests
116
+
117
+ Event stream procedures can be cancelled from inside one of the hooks by throwing a `CancellationException`
118
+
119
+ ```kotlin
120
+ runBlocking {
121
+ myClient.users.watchUserChanges(
122
+ onResponseError { err ->
123
+ println("Server returned an error: ${err.code} ${err.message}")
124
+ throw CancellationException()
125
+ }
126
+ )
127
+ }
128
+ ```
129
+
130
+ You can also spawn a job and cancel that job in order to cancel the Event stream procedure from the outside
131
+
132
+ ```kotlin
133
+ val job = someCoroutineScope.launch {
134
+ myClient.users.watchUserChanges()
135
+ }
136
+ job.cancel()
137
+ ```
138
+
139
+ ##### Other Options
140
+
141
+ | Option | Type | Description |
142
+ | -------------- | ----- | ------------------------------------------------------------------------------------------------------- |
143
+ | bufferCapacity | Int | Max buffer size that can be allocated towards reading messages received. Default is 1024 \* 1024 (1MB). |
144
+ | maxBackoffTime | Long? | Max wait time between retries in milliseconds. Default is 30000ms |
145
+
75
146
  ### Using Arri Models
76
147
 
77
148
  All generated models will be data classes. They will have access to the following features:
package/dist/index.cjs CHANGED
@@ -65,7 +65,7 @@ function getClassName(schema, context) {
65
65
  normalize: true
66
66
  })
67
67
  );
68
- return `${context.modelPrefix}${className2}`;
68
+ return `${context.typePrefix}${className2}`;
69
69
  }
70
70
  const depth = instanceDepth(context);
71
71
  if (depth === 1 && !context.discriminatorKey) {
@@ -74,7 +74,7 @@ function getClassName(schema, context) {
74
74
  normalize: true
75
75
  })
76
76
  );
77
- return `${context.modelPrefix}${className2}`;
77
+ return `${context.typePrefix}${className2}`;
78
78
  }
79
79
  if (context.discriminatorParentId && context.discriminatorKey && context.discriminatorValue) {
80
80
  const className2 = kotlinClassName(
@@ -83,7 +83,7 @@ function getClassName(schema, context) {
83
83
  { normalize: true }
84
84
  )
85
85
  );
86
- return `${context.modelPrefix}${className2}`;
86
+ return `${context.typePrefix}${className2}`;
87
87
  }
88
88
  const className = kotlinClassName(
89
89
  codegenUtils.pascalCase(
@@ -93,7 +93,7 @@ function getClassName(schema, context) {
93
93
  }
94
94
  )
95
95
  );
96
- return `${context.modelPrefix}${className}`;
96
+ return `${context.typePrefix}${className}`;
97
97
  }
98
98
  function instanceDepth(context) {
99
99
  const parts = context.instancePath.split("/");
@@ -172,7 +172,7 @@ function kotlinArrayFromSchema(schema, context) {
172
172
  const nullable = isNullable(schema, context);
173
173
  const defaultValue = nullable ? "null" : "mutableListOf()";
174
174
  const subType = kotlinTypeFromSchema(schema.elements, {
175
- modelPrefix: context.modelPrefix,
175
+ typePrefix: context.typePrefix,
176
176
  clientName: context.clientName,
177
177
  clientVersion: context.clientVersion,
178
178
  instancePath: `${context.instancePath}/[Element]`,
@@ -313,7 +313,7 @@ function kotlinObjectFromSchema(schema, context) {
313
313
  kotlinKeys.push(kotlinKey);
314
314
  const prop = schema.properties[key];
315
315
  const type = kotlinTypeFromSchema(prop, {
316
- modelPrefix: context.modelPrefix,
316
+ typePrefix: context.typePrefix,
317
317
  clientName: context.clientName,
318
318
  clientVersion: context.clientVersion,
319
319
  instancePath: `/${className}/${key}`,
@@ -347,7 +347,7 @@ function kotlinObjectFromSchema(schema, context) {
347
347
  const kotlinKey = kotlinIdentifier(key);
348
348
  kotlinKeys.push(kotlinKey);
349
349
  const type = kotlinTypeFromSchema(schema.optionalProperties[key], {
350
- modelPrefix: context.modelPrefix,
350
+ typePrefix: context.typePrefix,
351
351
  clientName: context.clientName,
352
352
  clientVersion: context.clientVersion,
353
353
  instancePath: `/${className}/${key}`,
@@ -446,7 +446,7 @@ function kotlinDiscriminatorFromSchema(schema, context) {
446
446
  for (const key of Object.keys(schema.mapping)) {
447
447
  const subSchema = schema.mapping[key];
448
448
  const subType = kotlinObjectFromSchema(subSchema, {
449
- modelPrefix: context.modelPrefix,
449
+ typePrefix: context.typePrefix,
450
450
  clientName: context.clientName,
451
451
  clientVersion: context.clientVersion,
452
452
  instancePath: context.instancePath,
@@ -634,7 +634,7 @@ function kotlinEnumFromSchema(schema, context) {
634
634
  function kotlinMapFromSchema(schema, context) {
635
635
  const nullable = isNullable(schema, context);
636
636
  const subType = kotlinTypeFromSchema(schema.values, {
637
- modelPrefix: context.modelPrefix,
637
+ typePrefix: context.typePrefix,
638
638
  clientName: context.clientName,
639
639
  clientVersion: context.clientVersion,
640
640
  instancePath: `${context.instancePath}/[value]`,
@@ -1129,8 +1129,8 @@ function kotlinProcedureFromSchema(schema, context) {
1129
1129
  }
1130
1130
  function kotlinHttpRpcFromSchema(schema, context) {
1131
1131
  const name = getProcedureName(context);
1132
- const params = schema.params ? kotlinClassName(`${context.modelPrefix}_${schema.params}`) : void 0;
1133
- const response = schema.response ? kotlinClassName(`${context.modelPrefix}_${schema.response}`) : void 0;
1132
+ const params = schema.params ? kotlinClassName(`${context.typePrefix}_${schema.params}`) : void 0;
1133
+ const response = schema.response ? kotlinClassName(`${context.typePrefix}_${schema.response}`) : void 0;
1134
1134
  const codeComment = getCodeComment(
1135
1135
  {
1136
1136
  description: schema.description,
@@ -1140,40 +1140,36 @@ function kotlinHttpRpcFromSchema(schema, context) {
1140
1140
  "method"
1141
1141
  );
1142
1142
  if (schema.isEventStream) {
1143
- return `${codeComment}fun ${name}(
1144
- scope: CoroutineScope,
1143
+ return `${codeComment}suspend fun ${name}(
1145
1144
  ${params ? `params: ${params},` : ""}
1146
1145
  lastEventId: String? = null,
1147
- bufferCapacity: Int = 1024,
1146
+ bufferCapacity: Int = 1024 * 1024,
1148
1147
  onOpen: ((response: HttpResponse) -> Unit) = {},
1149
1148
  onClose: (() -> Unit) = {},
1150
- onError: ((error: ${context.clientName}Error) -> Unit) = {},
1151
- onConnectionError: ((error: ${context.clientName}Error) -> Unit) = {},
1149
+ onRequestError: ((error: Exception) -> Unit) = {},
1150
+ onResponseError: ((error: ${context.clientName}Error) -> Unit) = {},
1152
1151
  onData: ((${response ? `data: ${response}` : ""}) -> Unit) = {},
1153
- ): Job {
1154
- val job = scope.launch {
1155
- __handleSseRequest(
1156
- scope = scope,
1157
- httpClient = httpClient,
1158
- url = "$baseUrl${schema.path}",
1159
- method = HttpMethod.${codegenUtils.pascalCase(schema.method, { normalize: true })},
1160
- params = ${params ? "params" : "null"},
1161
- headers = headers,
1162
- backoffTime = 0,
1163
- maxBackoffTime = 30000L,
1164
- lastEventId = lastEventId,
1165
- bufferCapacity = bufferCapacity,
1166
- onOpen = onOpen,
1167
- onClose = onClose,
1168
- onError = onError,
1169
- onConnectionError = onConnectionError,
1170
- onData = { str ->
1171
- ${response ? `val data = ${response}.fromJson(str)` : ""}
1172
- onData(${response ? "data" : ""})
1173
- }
1174
- )
1175
- }
1176
- return job
1152
+ maxBackoffTime: Long? = null,
1153
+ ): Unit {
1154
+ __handleSseRequest(
1155
+ httpClient = httpClient,
1156
+ url = "$baseUrl${schema.path}",
1157
+ method = HttpMethod.${codegenUtils.pascalCase(schema.method, { normalize: true })},
1158
+ params = ${params ? "params" : "null"},
1159
+ headers = headers,
1160
+ backoffTime = 0,
1161
+ maxBackoffTime = maxBackoffTime ?: 30000L,
1162
+ lastEventId = lastEventId,
1163
+ bufferCapacity = bufferCapacity,
1164
+ onOpen = onOpen,
1165
+ onClose = onClose,
1166
+ onRequestError = onRequestError,
1167
+ onResponseError = onResponseError,
1168
+ onData = { str ->
1169
+ ${response ? `val data = ${response}.fromJson(str)` : ""}
1170
+ onData(${response ? "data" : ""})
1171
+ }
1172
+ )
1177
1173
  }`;
1178
1174
  }
1179
1175
  const headingCheck = `if (response.headers["Content-Type"] != "application/json") {
@@ -1301,7 +1297,7 @@ const kotlinClientGenerator = codegenUtils.defineGeneratorPlugin(
1301
1297
  function kotlinClientFromAppDefinition(def, options) {
1302
1298
  const clientName = kotlinClassName(options.clientName ?? "Client");
1303
1299
  const context = {
1304
- modelPrefix: options.modelPrefix ?? "",
1300
+ typePrefix: options.typePrefix ?? "",
1305
1301
  clientName,
1306
1302
  clientVersion: def.info?.version ?? "",
1307
1303
  instancePath: "",
@@ -1312,7 +1308,7 @@ function kotlinClientFromAppDefinition(def, options) {
1312
1308
  for (const key of Object.keys(def.definitions)) {
1313
1309
  const subSchema = def.definitions[key];
1314
1310
  const model = kotlinTypeFromSchema(subSchema, {
1315
- modelPrefix: context.modelPrefix,
1311
+ typePrefix: context.typePrefix,
1316
1312
  clientName: context.clientName,
1317
1313
  clientVersion: context.clientVersion,
1318
1314
  instancePath: `/${key}`,
@@ -1644,62 +1640,89 @@ private suspend fun __prepareRequest(
1644
1640
  return client.prepareRequest(builder)
1645
1641
  }
1646
1642
 
1647
- private fun __parseSseEvent(input: String): __SseEvent {
1648
- val lines = input.split("\\n")
1649
- var id: String? = null
1650
- var event: String? = null
1651
- var data: String = ""
1652
- for (line in lines) {
1653
- if (line.startsWith("id: ")) {
1654
- id = line.substring(3).trim()
1655
- continue
1656
- }
1657
- if (line.startsWith("event: ")) {
1658
- event = line.substring(6).trim()
1659
- continue
1660
- }
1661
- if (line.startsWith("data: ")) {
1662
- data = line.substring(5).trim()
1663
- continue
1664
- }
1643
+ // SSE_FN_START
1644
+ private enum class SseEventLineType {
1645
+ Id,
1646
+ Event,
1647
+ Data,
1648
+ Retry,
1649
+ None,
1650
+ }
1651
+
1652
+ private fun __parseSseEventLine(line: String): Pair<SseEventLineType, String> {
1653
+ if (line.startsWith("id:")) {
1654
+ return Pair(SseEventLineType.Id, line.substring(3).trim())
1655
+ }
1656
+ if (line.startsWith("event:")) {
1657
+ return Pair(SseEventLineType.Event, line.substring(6).trim())
1658
+ }
1659
+ if (line.startsWith("data:")) {
1660
+ return Pair(SseEventLineType.Data, line.substring(5).trim())
1661
+ }
1662
+ if (line.startsWith("retry:")) {
1663
+ return Pair(SseEventLineType.Retry, line.substring(6).trim())
1665
1664
  }
1666
- return __SseEvent(id, event, data)
1665
+ return Pair(SseEventLineType.None, "")
1667
1666
  }
1668
1667
 
1669
- private class __SseEvent(val id: String? = null, val event: String? = null, val data: String)
1668
+ private data class __SseEvent(
1669
+ val id: String? = null,
1670
+ val event: String,
1671
+ val data: String,
1672
+ val retry: Int? = null
1673
+ )
1670
1674
 
1671
1675
  private class __SseEventParsingResult(val events: List<__SseEvent>, val leftover: String)
1672
1676
 
1673
1677
  private fun __parseSseEvents(input: String): __SseEventParsingResult {
1674
- val inputs = input.split("\\n\\n").toMutableList()
1675
- if (inputs.isEmpty()) {
1676
- return __SseEventParsingResult(
1677
- events = listOf(),
1678
- leftover = "",
1679
- )
1680
- }
1681
- if (inputs.size == 1) {
1682
- return __SseEventParsingResult(
1683
- events = listOf(),
1684
- leftover = inputs.last(),
1685
- )
1686
- }
1687
- val leftover = inputs.last()
1688
- inputs.removeLast()
1689
1678
  val events = mutableListOf<__SseEvent>()
1690
- for (item in inputs) {
1691
- if (item.contains("data: ")) {
1692
- events.add(__parseSseEvent(item))
1679
+ val lines = input.lines()
1680
+ if (lines.isEmpty()) {
1681
+ return __SseEventParsingResult(events = listOf(), leftover = "")
1682
+ }
1683
+ var id: String? = null
1684
+ var event: String? = null
1685
+ var data: String? = null
1686
+ var retry: Int? = null
1687
+ var lastIndex: Int? = 0
1688
+ lines.forEachIndexed { index, line ->
1689
+ if (line.isNotEmpty()) {
1690
+ val (type, value) = __parseSseEventLine(line)
1691
+ when (type) {
1692
+ SseEventLineType.Id -> id = value
1693
+ SseEventLineType.Event -> event = value
1694
+ SseEventLineType.Data -> data = value
1695
+ SseEventLineType.Retry -> retry = value.toInt()
1696
+ SseEventLineType.None -> {}
1697
+ }
1698
+ }
1699
+ val isEnd = line == ""
1700
+ if (isEnd) {
1701
+ if (data != null) {
1702
+ events.add(
1703
+ __SseEvent(
1704
+ id = id,
1705
+ event = event ?: "message",
1706
+ data = data!!,
1707
+ retry = retry,
1708
+ )
1709
+ )
1710
+ }
1711
+ id = null
1712
+ event = null
1713
+ data = null
1714
+ retry = null
1715
+ lastIndex = if (index + 1 < lines.size) index + 1 else null
1693
1716
  }
1694
1717
  }
1695
1718
  return __SseEventParsingResult(
1696
1719
  events = events,
1697
- leftover = leftover,
1720
+ leftover = if (lastIndex != null) lines.subList(lastIndex!!, lines.size).joinToString(separator = "\\n") else ""
1698
1721
  )
1699
1722
  }
1723
+ // SSE_FN_END
1700
1724
 
1701
1725
  private suspend fun __handleSseRequest(
1702
- scope: CoroutineScope,
1703
1726
  httpClient: HttpClient,
1704
1727
  url: String,
1705
1728
  method: HttpMethod,
@@ -1710,16 +1733,16 @@ private suspend fun __handleSseRequest(
1710
1733
  lastEventId: String?,
1711
1734
  onOpen: ((response: HttpResponse) -> Unit) = {},
1712
1735
  onClose: (() -> Unit) = {},
1713
- onError: ((error: ${clientName}Error) -> Unit) = {},
1714
1736
  onData: ((data: String) -> Unit) = {},
1715
- onConnectionError: ((error: ${clientName}Error) -> Unit) = {},
1737
+ onRequestError: ((error: Exception) -> Unit) = {},
1738
+ onResponseError: ((error: ${clientName}Error) -> Unit) = {},
1716
1739
  bufferCapacity: Int,
1717
1740
  ) {
1718
1741
  val finalHeaders = headers?.invoke() ?: mutableMapOf()
1719
1742
  var lastId = lastEventId
1720
1743
  // exponential backoff maxing out at 32 seconds
1721
1744
  if (backoffTime > 0) {
1722
- withContext(scope.coroutineContext) {
1745
+ withContext(currentCoroutineContext()) {
1723
1746
  Thread.sleep(backoffTime)
1724
1747
  }
1725
1748
  }
@@ -1741,16 +1764,17 @@ private suspend fun __handleSseRequest(
1741
1764
  onOpen(httpResponse)
1742
1765
  } catch (e: CancellationException) {
1743
1766
  onClose()
1767
+ httpResponse.cancel()
1744
1768
  return@execute
1745
1769
  }
1746
1770
  if (httpResponse.status.value !in 200..299) {
1747
1771
  try {
1748
1772
  if (httpResponse.headers["Content-Type"] == "application/json") {
1749
- onConnectionError(
1773
+ onResponseError(
1750
1774
  ${clientName}Error.fromJson(httpResponse.bodyAsText())
1751
1775
  )
1752
1776
  } else {
1753
- onConnectionError(
1777
+ onResponseError(
1754
1778
  ${clientName}Error(
1755
1779
  code = httpResponse.status.value,
1756
1780
  errorMessage = httpResponse.status.description,
@@ -1761,10 +1785,10 @@ private suspend fun __handleSseRequest(
1761
1785
  }
1762
1786
  } catch (e: CancellationException) {
1763
1787
  onClose()
1788
+ httpResponse.cancel()
1764
1789
  return@execute
1765
1790
  }
1766
- __handleSseRequest(
1767
- scope = scope,
1791
+ return@execute __handleSseRequest(
1768
1792
  httpClient = httpClient,
1769
1793
  url = url,
1770
1794
  method = method,
@@ -1776,15 +1800,13 @@ private suspend fun __handleSseRequest(
1776
1800
  bufferCapacity = bufferCapacity,
1777
1801
  onOpen = onOpen,
1778
1802
  onClose = onClose,
1779
- onError = onError,
1780
1803
  onData = onData,
1781
- onConnectionError = onConnectionError,
1804
+ onResponseError = onResponseError,
1782
1805
  )
1783
- return@execute
1784
1806
  }
1785
1807
  if (httpResponse.headers["Content-Type"] != "text/event-stream") {
1786
1808
  try {
1787
- onConnectionError(
1809
+ onResponseError(
1788
1810
  ${clientName}Error(
1789
1811
  code = 0,
1790
1812
  errorMessage = "Expected server to return Content-Type \\"text/event-stream\\". Got \\"\${httpResponse.headers["Content-Type"]}\\"",
@@ -1793,10 +1815,10 @@ private suspend fun __handleSseRequest(
1793
1815
  )
1794
1816
  )
1795
1817
  } catch (e: CancellationException) {
1818
+ httpResponse.cancel()
1796
1819
  return@execute
1797
1820
  }
1798
- __handleSseRequest(
1799
- scope = scope,
1821
+ return@execute __handleSseRequest(
1800
1822
  httpClient = httpClient,
1801
1823
  url = url,
1802
1824
  method = method,
@@ -1808,14 +1830,12 @@ private suspend fun __handleSseRequest(
1808
1830
  bufferCapacity = bufferCapacity,
1809
1831
  onOpen = onOpen,
1810
1832
  onClose = onClose,
1811
- onError = onError,
1812
1833
  onData = onData,
1813
- onConnectionError = onConnectionError,
1834
+ onResponseError = onResponseError,
1814
1835
  )
1815
- return@execute
1816
1836
  }
1817
1837
  newBackoffTime = 0
1818
- val channel: ByteReadChannel = httpResponse.bodyAsChannel()
1838
+ val channel: ByteReadChannel = httpResponse.body()
1819
1839
  var pendingData = ""
1820
1840
  while (!channel.isClosedForRead) {
1821
1841
  val buffer = ByteBuffer.allocateDirect(bufferCapacity)
@@ -1835,6 +1855,7 @@ private suspend fun __handleSseRequest(
1835
1855
  onData(event.data)
1836
1856
  } catch (e: CancellationException) {
1837
1857
  onClose()
1858
+ httpResponse.cancel()
1838
1859
  return@execute
1839
1860
  }
1840
1861
  }
@@ -1844,22 +1865,11 @@ private suspend fun __handleSseRequest(
1844
1865
  return@execute
1845
1866
  }
1846
1867
 
1847
- "error" -> {
1848
- val error = ${clientName}Error.fromJson(event.data)
1849
- try {
1850
- onError(error)
1851
- } catch (e: CancellationException) {
1852
- onClose()
1853
- return@execute
1854
- }
1855
- }
1856
-
1857
1868
  else -> {}
1858
1869
  }
1859
1870
  }
1860
1871
  }
1861
- __handleSseRequest(
1862
- scope = scope,
1872
+ return@execute __handleSseRequest(
1863
1873
  httpClient = httpClient,
1864
1874
  url = url,
1865
1875
  method = method,
@@ -1871,22 +1881,13 @@ private suspend fun __handleSseRequest(
1871
1881
  bufferCapacity = bufferCapacity,
1872
1882
  onOpen = onOpen,
1873
1883
  onClose = onClose,
1874
- onError = onError,
1875
1884
  onData = onData,
1876
- onConnectionError = onConnectionError,
1885
+ onResponseError = onResponseError,
1877
1886
  )
1878
1887
  }
1879
1888
  } catch (e: java.net.ConnectException) {
1880
- onConnectionError(
1881
- ${clientName}Error(
1882
- code = 503,
1883
- errorMessage = if (e.message != null) e.message!! else "Error connecting to $url",
1884
- data = JsonPrimitive(e.toString()),
1885
- stack = e.stackTraceToString().split("\\n"),
1886
- )
1887
- )
1888
- __handleSseRequest(
1889
- scope = scope,
1889
+ onRequestError(e)
1890
+ return __handleSseRequest(
1890
1891
  httpClient = httpClient,
1891
1892
  url = url,
1892
1893
  method = method,
@@ -1898,14 +1899,12 @@ private suspend fun __handleSseRequest(
1898
1899
  bufferCapacity = bufferCapacity,
1899
1900
  onOpen = onOpen,
1900
1901
  onClose = onClose,
1901
- onError = onError,
1902
1902
  onData = onData,
1903
- onConnectionError = onConnectionError,
1903
+ onResponseError = onResponseError,
1904
1904
  )
1905
- return
1906
1905
  } catch (e: Exception) {
1907
- __handleSseRequest(
1908
- scope = scope,
1906
+ onRequestError(e)
1907
+ return __handleSseRequest(
1909
1908
  httpClient = httpClient,
1910
1909
  url = url,
1911
1910
  method = method,
@@ -1917,9 +1916,8 @@ private suspend fun __handleSseRequest(
1917
1916
  bufferCapacity = bufferCapacity,
1918
1917
  onOpen = onOpen,
1919
1918
  onClose = onClose,
1920
- onError = onError,
1921
1919
  onData = onData,
1922
- onConnectionError = onConnectionError,
1920
+ onResponseError = onResponseError,
1923
1921
  )
1924
1922
  }
1925
1923
  }`;
@@ -1932,14 +1930,14 @@ function getHeader(options) {
1932
1930
  )
1933
1931
 
1934
1932
  import io.ktor.client.*
1933
+ import io.ktor.client.call.*
1935
1934
  import io.ktor.client.plugins.*
1936
1935
  import io.ktor.client.request.*
1937
1936
  import io.ktor.client.statement.*
1938
1937
  import io.ktor.http.*
1939
1938
  import io.ktor.utils.io.*
1940
- import kotlinx.coroutines.CoroutineScope
1941
- import kotlinx.coroutines.Job
1942
- import kotlinx.coroutines.launch
1939
+ import kotlinx.coroutines.cancel
1940
+ import kotlinx.coroutines.currentCoroutineContext
1943
1941
  import kotlinx.coroutines.withContext
1944
1942
  import kotlinx.serialization.encodeToString
1945
1943
  import kotlinx.serialization.json.*
package/dist/index.d.cts CHANGED
@@ -2,7 +2,7 @@ import * as _arrirpc_codegen_utils from '@arrirpc/codegen-utils';
2
2
  import { AppDefinition, Schema } from '@arrirpc/codegen-utils';
3
3
 
4
4
  interface CodegenContext {
5
- modelPrefix: string;
5
+ typePrefix: string;
6
6
  clientName: string;
7
7
  clientVersion: string;
8
8
  instancePath: string;
@@ -25,12 +25,12 @@ interface KotlinProperty {
25
25
 
26
26
  interface ServiceContext {
27
27
  clientName: string;
28
- modelPrefix?: string;
28
+ typePrefix?: string;
29
29
  modelJsonInstances: Record<string, string>;
30
30
  }
31
31
  interface KotlinClientOptions {
32
32
  clientName?: string;
33
- modelPrefix?: string;
33
+ typePrefix?: string;
34
34
  outputFile: string;
35
35
  }
36
36
  declare const kotlinClientGenerator: _arrirpc_codegen_utils.GeneratorPlugin<KotlinClientOptions>;
package/dist/index.d.mts CHANGED
@@ -2,7 +2,7 @@ import * as _arrirpc_codegen_utils from '@arrirpc/codegen-utils';
2
2
  import { AppDefinition, Schema } from '@arrirpc/codegen-utils';
3
3
 
4
4
  interface CodegenContext {
5
- modelPrefix: string;
5
+ typePrefix: string;
6
6
  clientName: string;
7
7
  clientVersion: string;
8
8
  instancePath: string;
@@ -25,12 +25,12 @@ interface KotlinProperty {
25
25
 
26
26
  interface ServiceContext {
27
27
  clientName: string;
28
- modelPrefix?: string;
28
+ typePrefix?: string;
29
29
  modelJsonInstances: Record<string, string>;
30
30
  }
31
31
  interface KotlinClientOptions {
32
32
  clientName?: string;
33
- modelPrefix?: string;
33
+ typePrefix?: string;
34
34
  outputFile: string;
35
35
  }
36
36
  declare const kotlinClientGenerator: _arrirpc_codegen_utils.GeneratorPlugin<KotlinClientOptions>;
package/dist/index.d.ts CHANGED
@@ -2,7 +2,7 @@ import * as _arrirpc_codegen_utils from '@arrirpc/codegen-utils';
2
2
  import { AppDefinition, Schema } from '@arrirpc/codegen-utils';
3
3
 
4
4
  interface CodegenContext {
5
- modelPrefix: string;
5
+ typePrefix: string;
6
6
  clientName: string;
7
7
  clientVersion: string;
8
8
  instancePath: string;
@@ -25,12 +25,12 @@ interface KotlinProperty {
25
25
 
26
26
  interface ServiceContext {
27
27
  clientName: string;
28
- modelPrefix?: string;
28
+ typePrefix?: string;
29
29
  modelJsonInstances: Record<string, string>;
30
30
  }
31
31
  interface KotlinClientOptions {
32
32
  clientName?: string;
33
- modelPrefix?: string;
33
+ typePrefix?: string;
34
34
  outputFile: string;
35
35
  }
36
36
  declare const kotlinClientGenerator: _arrirpc_codegen_utils.GeneratorPlugin<KotlinClientOptions>;
package/dist/index.mjs CHANGED
@@ -59,7 +59,7 @@ function getClassName(schema, context) {
59
59
  normalize: true
60
60
  })
61
61
  );
62
- return `${context.modelPrefix}${className2}`;
62
+ return `${context.typePrefix}${className2}`;
63
63
  }
64
64
  const depth = instanceDepth(context);
65
65
  if (depth === 1 && !context.discriminatorKey) {
@@ -68,7 +68,7 @@ function getClassName(schema, context) {
68
68
  normalize: true
69
69
  })
70
70
  );
71
- return `${context.modelPrefix}${className2}`;
71
+ return `${context.typePrefix}${className2}`;
72
72
  }
73
73
  if (context.discriminatorParentId && context.discriminatorKey && context.discriminatorValue) {
74
74
  const className2 = kotlinClassName(
@@ -77,7 +77,7 @@ function getClassName(schema, context) {
77
77
  { normalize: true }
78
78
  )
79
79
  );
80
- return `${context.modelPrefix}${className2}`;
80
+ return `${context.typePrefix}${className2}`;
81
81
  }
82
82
  const className = kotlinClassName(
83
83
  pascalCase(
@@ -87,7 +87,7 @@ function getClassName(schema, context) {
87
87
  }
88
88
  )
89
89
  );
90
- return `${context.modelPrefix}${className}`;
90
+ return `${context.typePrefix}${className}`;
91
91
  }
92
92
  function instanceDepth(context) {
93
93
  const parts = context.instancePath.split("/");
@@ -166,7 +166,7 @@ function kotlinArrayFromSchema(schema, context) {
166
166
  const nullable = isNullable(schema, context);
167
167
  const defaultValue = nullable ? "null" : "mutableListOf()";
168
168
  const subType = kotlinTypeFromSchema(schema.elements, {
169
- modelPrefix: context.modelPrefix,
169
+ typePrefix: context.typePrefix,
170
170
  clientName: context.clientName,
171
171
  clientVersion: context.clientVersion,
172
172
  instancePath: `${context.instancePath}/[Element]`,
@@ -307,7 +307,7 @@ function kotlinObjectFromSchema(schema, context) {
307
307
  kotlinKeys.push(kotlinKey);
308
308
  const prop = schema.properties[key];
309
309
  const type = kotlinTypeFromSchema(prop, {
310
- modelPrefix: context.modelPrefix,
310
+ typePrefix: context.typePrefix,
311
311
  clientName: context.clientName,
312
312
  clientVersion: context.clientVersion,
313
313
  instancePath: `/${className}/${key}`,
@@ -341,7 +341,7 @@ function kotlinObjectFromSchema(schema, context) {
341
341
  const kotlinKey = kotlinIdentifier(key);
342
342
  kotlinKeys.push(kotlinKey);
343
343
  const type = kotlinTypeFromSchema(schema.optionalProperties[key], {
344
- modelPrefix: context.modelPrefix,
344
+ typePrefix: context.typePrefix,
345
345
  clientName: context.clientName,
346
346
  clientVersion: context.clientVersion,
347
347
  instancePath: `/${className}/${key}`,
@@ -440,7 +440,7 @@ function kotlinDiscriminatorFromSchema(schema, context) {
440
440
  for (const key of Object.keys(schema.mapping)) {
441
441
  const subSchema = schema.mapping[key];
442
442
  const subType = kotlinObjectFromSchema(subSchema, {
443
- modelPrefix: context.modelPrefix,
443
+ typePrefix: context.typePrefix,
444
444
  clientName: context.clientName,
445
445
  clientVersion: context.clientVersion,
446
446
  instancePath: context.instancePath,
@@ -628,7 +628,7 @@ function kotlinEnumFromSchema(schema, context) {
628
628
  function kotlinMapFromSchema(schema, context) {
629
629
  const nullable = isNullable(schema, context);
630
630
  const subType = kotlinTypeFromSchema(schema.values, {
631
- modelPrefix: context.modelPrefix,
631
+ typePrefix: context.typePrefix,
632
632
  clientName: context.clientName,
633
633
  clientVersion: context.clientVersion,
634
634
  instancePath: `${context.instancePath}/[value]`,
@@ -1123,8 +1123,8 @@ function kotlinProcedureFromSchema(schema, context) {
1123
1123
  }
1124
1124
  function kotlinHttpRpcFromSchema(schema, context) {
1125
1125
  const name = getProcedureName(context);
1126
- const params = schema.params ? kotlinClassName(`${context.modelPrefix}_${schema.params}`) : void 0;
1127
- const response = schema.response ? kotlinClassName(`${context.modelPrefix}_${schema.response}`) : void 0;
1126
+ const params = schema.params ? kotlinClassName(`${context.typePrefix}_${schema.params}`) : void 0;
1127
+ const response = schema.response ? kotlinClassName(`${context.typePrefix}_${schema.response}`) : void 0;
1128
1128
  const codeComment = getCodeComment(
1129
1129
  {
1130
1130
  description: schema.description,
@@ -1134,40 +1134,36 @@ function kotlinHttpRpcFromSchema(schema, context) {
1134
1134
  "method"
1135
1135
  );
1136
1136
  if (schema.isEventStream) {
1137
- return `${codeComment}fun ${name}(
1138
- scope: CoroutineScope,
1137
+ return `${codeComment}suspend fun ${name}(
1139
1138
  ${params ? `params: ${params},` : ""}
1140
1139
  lastEventId: String? = null,
1141
- bufferCapacity: Int = 1024,
1140
+ bufferCapacity: Int = 1024 * 1024,
1142
1141
  onOpen: ((response: HttpResponse) -> Unit) = {},
1143
1142
  onClose: (() -> Unit) = {},
1144
- onError: ((error: ${context.clientName}Error) -> Unit) = {},
1145
- onConnectionError: ((error: ${context.clientName}Error) -> Unit) = {},
1143
+ onRequestError: ((error: Exception) -> Unit) = {},
1144
+ onResponseError: ((error: ${context.clientName}Error) -> Unit) = {},
1146
1145
  onData: ((${response ? `data: ${response}` : ""}) -> Unit) = {},
1147
- ): Job {
1148
- val job = scope.launch {
1149
- __handleSseRequest(
1150
- scope = scope,
1151
- httpClient = httpClient,
1152
- url = "$baseUrl${schema.path}",
1153
- method = HttpMethod.${pascalCase(schema.method, { normalize: true })},
1154
- params = ${params ? "params" : "null"},
1155
- headers = headers,
1156
- backoffTime = 0,
1157
- maxBackoffTime = 30000L,
1158
- lastEventId = lastEventId,
1159
- bufferCapacity = bufferCapacity,
1160
- onOpen = onOpen,
1161
- onClose = onClose,
1162
- onError = onError,
1163
- onConnectionError = onConnectionError,
1164
- onData = { str ->
1165
- ${response ? `val data = ${response}.fromJson(str)` : ""}
1166
- onData(${response ? "data" : ""})
1167
- }
1168
- )
1169
- }
1170
- return job
1146
+ maxBackoffTime: Long? = null,
1147
+ ): Unit {
1148
+ __handleSseRequest(
1149
+ httpClient = httpClient,
1150
+ url = "$baseUrl${schema.path}",
1151
+ method = HttpMethod.${pascalCase(schema.method, { normalize: true })},
1152
+ params = ${params ? "params" : "null"},
1153
+ headers = headers,
1154
+ backoffTime = 0,
1155
+ maxBackoffTime = maxBackoffTime ?: 30000L,
1156
+ lastEventId = lastEventId,
1157
+ bufferCapacity = bufferCapacity,
1158
+ onOpen = onOpen,
1159
+ onClose = onClose,
1160
+ onRequestError = onRequestError,
1161
+ onResponseError = onResponseError,
1162
+ onData = { str ->
1163
+ ${response ? `val data = ${response}.fromJson(str)` : ""}
1164
+ onData(${response ? "data" : ""})
1165
+ }
1166
+ )
1171
1167
  }`;
1172
1168
  }
1173
1169
  const headingCheck = `if (response.headers["Content-Type"] != "application/json") {
@@ -1295,7 +1291,7 @@ const kotlinClientGenerator = defineGeneratorPlugin(
1295
1291
  function kotlinClientFromAppDefinition(def, options) {
1296
1292
  const clientName = kotlinClassName(options.clientName ?? "Client");
1297
1293
  const context = {
1298
- modelPrefix: options.modelPrefix ?? "",
1294
+ typePrefix: options.typePrefix ?? "",
1299
1295
  clientName,
1300
1296
  clientVersion: def.info?.version ?? "",
1301
1297
  instancePath: "",
@@ -1306,7 +1302,7 @@ function kotlinClientFromAppDefinition(def, options) {
1306
1302
  for (const key of Object.keys(def.definitions)) {
1307
1303
  const subSchema = def.definitions[key];
1308
1304
  const model = kotlinTypeFromSchema(subSchema, {
1309
- modelPrefix: context.modelPrefix,
1305
+ typePrefix: context.typePrefix,
1310
1306
  clientName: context.clientName,
1311
1307
  clientVersion: context.clientVersion,
1312
1308
  instancePath: `/${key}`,
@@ -1638,62 +1634,89 @@ private suspend fun __prepareRequest(
1638
1634
  return client.prepareRequest(builder)
1639
1635
  }
1640
1636
 
1641
- private fun __parseSseEvent(input: String): __SseEvent {
1642
- val lines = input.split("\\n")
1643
- var id: String? = null
1644
- var event: String? = null
1645
- var data: String = ""
1646
- for (line in lines) {
1647
- if (line.startsWith("id: ")) {
1648
- id = line.substring(3).trim()
1649
- continue
1650
- }
1651
- if (line.startsWith("event: ")) {
1652
- event = line.substring(6).trim()
1653
- continue
1654
- }
1655
- if (line.startsWith("data: ")) {
1656
- data = line.substring(5).trim()
1657
- continue
1658
- }
1637
+ // SSE_FN_START
1638
+ private enum class SseEventLineType {
1639
+ Id,
1640
+ Event,
1641
+ Data,
1642
+ Retry,
1643
+ None,
1644
+ }
1645
+
1646
+ private fun __parseSseEventLine(line: String): Pair<SseEventLineType, String> {
1647
+ if (line.startsWith("id:")) {
1648
+ return Pair(SseEventLineType.Id, line.substring(3).trim())
1649
+ }
1650
+ if (line.startsWith("event:")) {
1651
+ return Pair(SseEventLineType.Event, line.substring(6).trim())
1652
+ }
1653
+ if (line.startsWith("data:")) {
1654
+ return Pair(SseEventLineType.Data, line.substring(5).trim())
1655
+ }
1656
+ if (line.startsWith("retry:")) {
1657
+ return Pair(SseEventLineType.Retry, line.substring(6).trim())
1659
1658
  }
1660
- return __SseEvent(id, event, data)
1659
+ return Pair(SseEventLineType.None, "")
1661
1660
  }
1662
1661
 
1663
- private class __SseEvent(val id: String? = null, val event: String? = null, val data: String)
1662
+ private data class __SseEvent(
1663
+ val id: String? = null,
1664
+ val event: String,
1665
+ val data: String,
1666
+ val retry: Int? = null
1667
+ )
1664
1668
 
1665
1669
  private class __SseEventParsingResult(val events: List<__SseEvent>, val leftover: String)
1666
1670
 
1667
1671
  private fun __parseSseEvents(input: String): __SseEventParsingResult {
1668
- val inputs = input.split("\\n\\n").toMutableList()
1669
- if (inputs.isEmpty()) {
1670
- return __SseEventParsingResult(
1671
- events = listOf(),
1672
- leftover = "",
1673
- )
1674
- }
1675
- if (inputs.size == 1) {
1676
- return __SseEventParsingResult(
1677
- events = listOf(),
1678
- leftover = inputs.last(),
1679
- )
1680
- }
1681
- val leftover = inputs.last()
1682
- inputs.removeLast()
1683
1672
  val events = mutableListOf<__SseEvent>()
1684
- for (item in inputs) {
1685
- if (item.contains("data: ")) {
1686
- events.add(__parseSseEvent(item))
1673
+ val lines = input.lines()
1674
+ if (lines.isEmpty()) {
1675
+ return __SseEventParsingResult(events = listOf(), leftover = "")
1676
+ }
1677
+ var id: String? = null
1678
+ var event: String? = null
1679
+ var data: String? = null
1680
+ var retry: Int? = null
1681
+ var lastIndex: Int? = 0
1682
+ lines.forEachIndexed { index, line ->
1683
+ if (line.isNotEmpty()) {
1684
+ val (type, value) = __parseSseEventLine(line)
1685
+ when (type) {
1686
+ SseEventLineType.Id -> id = value
1687
+ SseEventLineType.Event -> event = value
1688
+ SseEventLineType.Data -> data = value
1689
+ SseEventLineType.Retry -> retry = value.toInt()
1690
+ SseEventLineType.None -> {}
1691
+ }
1692
+ }
1693
+ val isEnd = line == ""
1694
+ if (isEnd) {
1695
+ if (data != null) {
1696
+ events.add(
1697
+ __SseEvent(
1698
+ id = id,
1699
+ event = event ?: "message",
1700
+ data = data!!,
1701
+ retry = retry,
1702
+ )
1703
+ )
1704
+ }
1705
+ id = null
1706
+ event = null
1707
+ data = null
1708
+ retry = null
1709
+ lastIndex = if (index + 1 < lines.size) index + 1 else null
1687
1710
  }
1688
1711
  }
1689
1712
  return __SseEventParsingResult(
1690
1713
  events = events,
1691
- leftover = leftover,
1714
+ leftover = if (lastIndex != null) lines.subList(lastIndex!!, lines.size).joinToString(separator = "\\n") else ""
1692
1715
  )
1693
1716
  }
1717
+ // SSE_FN_END
1694
1718
 
1695
1719
  private suspend fun __handleSseRequest(
1696
- scope: CoroutineScope,
1697
1720
  httpClient: HttpClient,
1698
1721
  url: String,
1699
1722
  method: HttpMethod,
@@ -1704,16 +1727,16 @@ private suspend fun __handleSseRequest(
1704
1727
  lastEventId: String?,
1705
1728
  onOpen: ((response: HttpResponse) -> Unit) = {},
1706
1729
  onClose: (() -> Unit) = {},
1707
- onError: ((error: ${clientName}Error) -> Unit) = {},
1708
1730
  onData: ((data: String) -> Unit) = {},
1709
- onConnectionError: ((error: ${clientName}Error) -> Unit) = {},
1731
+ onRequestError: ((error: Exception) -> Unit) = {},
1732
+ onResponseError: ((error: ${clientName}Error) -> Unit) = {},
1710
1733
  bufferCapacity: Int,
1711
1734
  ) {
1712
1735
  val finalHeaders = headers?.invoke() ?: mutableMapOf()
1713
1736
  var lastId = lastEventId
1714
1737
  // exponential backoff maxing out at 32 seconds
1715
1738
  if (backoffTime > 0) {
1716
- withContext(scope.coroutineContext) {
1739
+ withContext(currentCoroutineContext()) {
1717
1740
  Thread.sleep(backoffTime)
1718
1741
  }
1719
1742
  }
@@ -1735,16 +1758,17 @@ private suspend fun __handleSseRequest(
1735
1758
  onOpen(httpResponse)
1736
1759
  } catch (e: CancellationException) {
1737
1760
  onClose()
1761
+ httpResponse.cancel()
1738
1762
  return@execute
1739
1763
  }
1740
1764
  if (httpResponse.status.value !in 200..299) {
1741
1765
  try {
1742
1766
  if (httpResponse.headers["Content-Type"] == "application/json") {
1743
- onConnectionError(
1767
+ onResponseError(
1744
1768
  ${clientName}Error.fromJson(httpResponse.bodyAsText())
1745
1769
  )
1746
1770
  } else {
1747
- onConnectionError(
1771
+ onResponseError(
1748
1772
  ${clientName}Error(
1749
1773
  code = httpResponse.status.value,
1750
1774
  errorMessage = httpResponse.status.description,
@@ -1755,10 +1779,10 @@ private suspend fun __handleSseRequest(
1755
1779
  }
1756
1780
  } catch (e: CancellationException) {
1757
1781
  onClose()
1782
+ httpResponse.cancel()
1758
1783
  return@execute
1759
1784
  }
1760
- __handleSseRequest(
1761
- scope = scope,
1785
+ return@execute __handleSseRequest(
1762
1786
  httpClient = httpClient,
1763
1787
  url = url,
1764
1788
  method = method,
@@ -1770,15 +1794,13 @@ private suspend fun __handleSseRequest(
1770
1794
  bufferCapacity = bufferCapacity,
1771
1795
  onOpen = onOpen,
1772
1796
  onClose = onClose,
1773
- onError = onError,
1774
1797
  onData = onData,
1775
- onConnectionError = onConnectionError,
1798
+ onResponseError = onResponseError,
1776
1799
  )
1777
- return@execute
1778
1800
  }
1779
1801
  if (httpResponse.headers["Content-Type"] != "text/event-stream") {
1780
1802
  try {
1781
- onConnectionError(
1803
+ onResponseError(
1782
1804
  ${clientName}Error(
1783
1805
  code = 0,
1784
1806
  errorMessage = "Expected server to return Content-Type \\"text/event-stream\\". Got \\"\${httpResponse.headers["Content-Type"]}\\"",
@@ -1787,10 +1809,10 @@ private suspend fun __handleSseRequest(
1787
1809
  )
1788
1810
  )
1789
1811
  } catch (e: CancellationException) {
1812
+ httpResponse.cancel()
1790
1813
  return@execute
1791
1814
  }
1792
- __handleSseRequest(
1793
- scope = scope,
1815
+ return@execute __handleSseRequest(
1794
1816
  httpClient = httpClient,
1795
1817
  url = url,
1796
1818
  method = method,
@@ -1802,14 +1824,12 @@ private suspend fun __handleSseRequest(
1802
1824
  bufferCapacity = bufferCapacity,
1803
1825
  onOpen = onOpen,
1804
1826
  onClose = onClose,
1805
- onError = onError,
1806
1827
  onData = onData,
1807
- onConnectionError = onConnectionError,
1828
+ onResponseError = onResponseError,
1808
1829
  )
1809
- return@execute
1810
1830
  }
1811
1831
  newBackoffTime = 0
1812
- val channel: ByteReadChannel = httpResponse.bodyAsChannel()
1832
+ val channel: ByteReadChannel = httpResponse.body()
1813
1833
  var pendingData = ""
1814
1834
  while (!channel.isClosedForRead) {
1815
1835
  val buffer = ByteBuffer.allocateDirect(bufferCapacity)
@@ -1829,6 +1849,7 @@ private suspend fun __handleSseRequest(
1829
1849
  onData(event.data)
1830
1850
  } catch (e: CancellationException) {
1831
1851
  onClose()
1852
+ httpResponse.cancel()
1832
1853
  return@execute
1833
1854
  }
1834
1855
  }
@@ -1838,22 +1859,11 @@ private suspend fun __handleSseRequest(
1838
1859
  return@execute
1839
1860
  }
1840
1861
 
1841
- "error" -> {
1842
- val error = ${clientName}Error.fromJson(event.data)
1843
- try {
1844
- onError(error)
1845
- } catch (e: CancellationException) {
1846
- onClose()
1847
- return@execute
1848
- }
1849
- }
1850
-
1851
1862
  else -> {}
1852
1863
  }
1853
1864
  }
1854
1865
  }
1855
- __handleSseRequest(
1856
- scope = scope,
1866
+ return@execute __handleSseRequest(
1857
1867
  httpClient = httpClient,
1858
1868
  url = url,
1859
1869
  method = method,
@@ -1865,22 +1875,13 @@ private suspend fun __handleSseRequest(
1865
1875
  bufferCapacity = bufferCapacity,
1866
1876
  onOpen = onOpen,
1867
1877
  onClose = onClose,
1868
- onError = onError,
1869
1878
  onData = onData,
1870
- onConnectionError = onConnectionError,
1879
+ onResponseError = onResponseError,
1871
1880
  )
1872
1881
  }
1873
1882
  } catch (e: java.net.ConnectException) {
1874
- onConnectionError(
1875
- ${clientName}Error(
1876
- code = 503,
1877
- errorMessage = if (e.message != null) e.message!! else "Error connecting to $url",
1878
- data = JsonPrimitive(e.toString()),
1879
- stack = e.stackTraceToString().split("\\n"),
1880
- )
1881
- )
1882
- __handleSseRequest(
1883
- scope = scope,
1883
+ onRequestError(e)
1884
+ return __handleSseRequest(
1884
1885
  httpClient = httpClient,
1885
1886
  url = url,
1886
1887
  method = method,
@@ -1892,14 +1893,12 @@ private suspend fun __handleSseRequest(
1892
1893
  bufferCapacity = bufferCapacity,
1893
1894
  onOpen = onOpen,
1894
1895
  onClose = onClose,
1895
- onError = onError,
1896
1896
  onData = onData,
1897
- onConnectionError = onConnectionError,
1897
+ onResponseError = onResponseError,
1898
1898
  )
1899
- return
1900
1899
  } catch (e: Exception) {
1901
- __handleSseRequest(
1902
- scope = scope,
1900
+ onRequestError(e)
1901
+ return __handleSseRequest(
1903
1902
  httpClient = httpClient,
1904
1903
  url = url,
1905
1904
  method = method,
@@ -1911,9 +1910,8 @@ private suspend fun __handleSseRequest(
1911
1910
  bufferCapacity = bufferCapacity,
1912
1911
  onOpen = onOpen,
1913
1912
  onClose = onClose,
1914
- onError = onError,
1915
1913
  onData = onData,
1916
- onConnectionError = onConnectionError,
1914
+ onResponseError = onResponseError,
1917
1915
  )
1918
1916
  }
1919
1917
  }`;
@@ -1926,14 +1924,14 @@ function getHeader(options) {
1926
1924
  )
1927
1925
 
1928
1926
  import io.ktor.client.*
1927
+ import io.ktor.client.call.*
1929
1928
  import io.ktor.client.plugins.*
1930
1929
  import io.ktor.client.request.*
1931
1930
  import io.ktor.client.statement.*
1932
1931
  import io.ktor.http.*
1933
1932
  import io.ktor.utils.io.*
1934
- import kotlinx.coroutines.CoroutineScope
1935
- import kotlinx.coroutines.Job
1936
- import kotlinx.coroutines.launch
1933
+ import kotlinx.coroutines.cancel
1934
+ import kotlinx.coroutines.currentCoroutineContext
1937
1935
  import kotlinx.coroutines.withContext
1938
1936
  import kotlinx.serialization.encodeToString
1939
1937
  import kotlinx.serialization.json.*
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arrirpc/codegen-kotlin",
3
- "version": "0.60.3",
3
+ "version": "0.61.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -22,6 +22,6 @@
22
22
  "dist"
23
23
  ],
24
24
  "dependencies": {
25
- "@arrirpc/codegen-utils": "0.60.3"
25
+ "@arrirpc/codegen-utils": "0.61.0"
26
26
  }
27
27
  }