@kizunajs/kotlin 2.0.0-beta.20 → 2.0.0-beta.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.cjs
CHANGED
package/dist/cli.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { t as generateKotlinClient } from "./generator-
|
|
2
|
+
import { t as generateKotlinClient } from "./generator-oVr_SgXs.mjs";
|
|
3
3
|
import { mkdirSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { dirname, resolve } from "node:path";
|
|
5
5
|
import { parseArgs } from "node:util";
|
|
@@ -551,18 +551,23 @@ const buildRouteMethod = (routeKey, route, registry, deprecated, deprecationMess
|
|
|
551
551
|
successResponses.push({
|
|
552
552
|
status,
|
|
553
553
|
type: stream.messageType ?? "Event",
|
|
554
|
-
responseHeaders: headersSchema ? collectObjectFields(headersSchema, registry, `${baseHint}ResponseHeaders`) : []
|
|
554
|
+
responseHeaders: headersSchema ? collectObjectFields(headersSchema, registry, `${baseHint}ResponseHeaders`) : [],
|
|
555
|
+
isText: false
|
|
555
556
|
});
|
|
556
557
|
continue;
|
|
557
558
|
}
|
|
558
|
-
const
|
|
559
|
+
const bodySchema = (0, kizunajs_generator.resolveResponseBody)(responseValue);
|
|
560
|
+
const responseContentType = (0, kizunajs_generator.resolveResponseContentType)(responseValue);
|
|
561
|
+
const isText = !(0, kizunajs_generator.isBinarySchema)(bodySchema) && responseContentType !== void 0 && !(0, kizunajs_generator.isJsonMediaType)(responseContentType);
|
|
562
|
+
const typeExpression = mapType(bodySchema, registry, responseHint).expression;
|
|
559
563
|
if ((0, kizunajs_generator.isSuccessStatus)(status)) {
|
|
560
564
|
const headersSchema = (0, kizunajs_generator.resolveResponseHeaders)(responseValue);
|
|
561
565
|
const perStatusHeaderFields = headersSchema ? collectObjectFields(headersSchema, registry, `${baseHint}ResponseHeaders`) : [];
|
|
562
566
|
successResponses.push({
|
|
563
567
|
status,
|
|
564
568
|
type: typeExpression,
|
|
565
|
-
responseHeaders: perStatusHeaderFields
|
|
569
|
+
responseHeaders: perStatusHeaderFields,
|
|
570
|
+
isText
|
|
566
571
|
});
|
|
567
572
|
} else errorCases.push({
|
|
568
573
|
caseName: (0, kizunajs_generator.statusToCamelCase)(status),
|
|
@@ -700,9 +705,12 @@ const KOTLIN_PRIMITIVE_TYPES = new Set([
|
|
|
700
705
|
"ByteArray"
|
|
701
706
|
]);
|
|
702
707
|
/**
|
|
703
|
-
* How a response body is read. Raw bytes are the body already.
|
|
708
|
+
* How a response body is read. Raw bytes are the body already, and text is its decoded bytes.
|
|
704
709
|
*/
|
|
705
|
-
const decodeBody = (resolved
|
|
710
|
+
const decodeBody = (resolved, isText = false) => {
|
|
711
|
+
if (resolved === "ByteArray") return "data";
|
|
712
|
+
return isText ? "data.decodeToString()" : `json.decodeFromString<${resolved}>(data.decodeToString())`;
|
|
713
|
+
};
|
|
706
714
|
const resolveType = (typeName, currentOperation, context, scope = "client") => {
|
|
707
715
|
const { operationTypeMap, namespaceName, clientName, fileLevelTypeNames } = context;
|
|
708
716
|
const optional = typeName.endsWith("?");
|
|
@@ -1413,7 +1421,7 @@ const emitMethodBody = (writer, method, context) => {
|
|
|
1413
1421
|
writer.indent(() => {
|
|
1414
1422
|
const resolved = resolveType(successResponse.type, method.operationName, context);
|
|
1415
1423
|
writer.block("try", () => {
|
|
1416
|
-
writer.line(`val payload = ${decodeBody(resolved)}`);
|
|
1424
|
+
writer.line(`val payload = ${decodeBody(resolved, successResponse.isText)}`);
|
|
1417
1425
|
const bodyExpr = isMultiSuccess ? `${operationRef}.Success.Status${successResponse.status}(payload)` : "payload";
|
|
1418
1426
|
if (hasHeaders) {
|
|
1419
1427
|
for (const field of successResponse.responseHeaders) writer.line(`val ${escapeKeyword(field.name)} = httpResponse.header(${stringLiteral(field.wireName)})`);
|
|
@@ -1476,6 +1484,35 @@ const emitErrorBranches = (writer, method, context) => {
|
|
|
1476
1484
|
}
|
|
1477
1485
|
writer.line(`else -> throw ${failureRef}.Unexpected(statusCode = statusCode, data = data)`);
|
|
1478
1486
|
};
|
|
1487
|
+
const IDEMPOTENT_METHODS = new Set([
|
|
1488
|
+
"GET",
|
|
1489
|
+
"HEAD",
|
|
1490
|
+
"OPTIONS",
|
|
1491
|
+
"PUT",
|
|
1492
|
+
"DELETE"
|
|
1493
|
+
]);
|
|
1494
|
+
/**
|
|
1495
|
+
* Writes the reconnection an event stream reopens through and returns the argument handing it to `Kizuna.events`, or
|
|
1496
|
+
* nothing for a method a client may not resend.
|
|
1497
|
+
*/
|
|
1498
|
+
const emitReopen = (writer, method, operationRef, status) => {
|
|
1499
|
+
if (!IDEMPOTENT_METHODS.has(method.method)) return "";
|
|
1500
|
+
closure(writer, "val reconnection = reconnect?.let { policy ->", () => {
|
|
1501
|
+
closure(writer, "Kizuna.Reconnection(policy) { lastEventId ->", () => {
|
|
1502
|
+
writer.line("val again = requestBuilder.build().newBuilder()");
|
|
1503
|
+
writer.line("if (lastEventId.isNotEmpty()) again.header(\"Last-Event-ID\", lastEventId)");
|
|
1504
|
+
writer.line("requestInterceptor?.invoke(again)");
|
|
1505
|
+
writer.line("val reopened = Kizuna.send(client, again.build(), retry)");
|
|
1506
|
+
writer.line("val eventStream = reopened.header(\"Content-Type\")?.lowercase()?.startsWith(\"text/event-stream\") == true");
|
|
1507
|
+
writer.block("when", () => {
|
|
1508
|
+
writer.line("reopened.code == 204 -> { reopened.close(); null }");
|
|
1509
|
+
writer.line(`reopened.code == ${status} && eventStream -> reopened`);
|
|
1510
|
+
writer.line(`else -> { reopened.close(); throw ${operationRef}.Failure.Unexpected(statusCode = reopened.code, data = ByteArray(0)) }`);
|
|
1511
|
+
});
|
|
1512
|
+
});
|
|
1513
|
+
});
|
|
1514
|
+
return ", reconnection";
|
|
1515
|
+
};
|
|
1479
1516
|
const emitStreamMethodTail = (writer, method, context) => {
|
|
1480
1517
|
const operationRef = `${context.clientName}.${method.operationName}`;
|
|
1481
1518
|
const stream = method.stream;
|
|
@@ -1488,7 +1525,7 @@ const emitStreamMethodTail = (writer, method, context) => {
|
|
|
1488
1525
|
else if (stream.events) {
|
|
1489
1526
|
const events = stream.events;
|
|
1490
1527
|
const elementType = streamElementType(method, context, "client");
|
|
1491
|
-
closure(writer, `val body = Kizuna.events<${elementType}>(httpResponse) { event ->`, () => {
|
|
1528
|
+
closure(writer, `val body = Kizuna.events<${elementType}>(httpResponse${emitReopen(writer, method, operationRef, stream.status)}) { event ->`, () => {
|
|
1492
1529
|
writer.block("when (event.event)", () => {
|
|
1493
1530
|
for (const event of events) {
|
|
1494
1531
|
const payloadType = resolveType(event.type, method.operationName, context);
|
|
@@ -1499,7 +1536,8 @@ const emitStreamMethodTail = (writer, method, context) => {
|
|
|
1499
1536
|
});
|
|
1500
1537
|
} else {
|
|
1501
1538
|
const elementType = streamElementType(method, context, "client");
|
|
1502
|
-
|
|
1539
|
+
const reconnection = emitReopen(writer, method, operationRef, stream.status);
|
|
1540
|
+
writer.line(`val body = Kizuna.events<${elementType}>(httpResponse${reconnection}) { event -> json.decodeFromString<${elementType}>(event.data) }`);
|
|
1503
1541
|
}
|
|
1504
1542
|
if (hasHeaders) {
|
|
1505
1543
|
for (const field of successResponse.responseHeaders) writer.line(`val ${escapeKeyword(field.name)} = httpResponse.header(${stringLiteral(field.wireName)})`);
|
|
@@ -1545,7 +1583,7 @@ const emitSubClientMethod = (writer, method, context) => {
|
|
|
1545
1583
|
};
|
|
1546
1584
|
const emitSubClientClass = (writer, group, clientName, context) => {
|
|
1547
1585
|
writer.blank();
|
|
1548
|
-
writer.block(`class ${group.className}(private val client: OkHttpClient, private val baseUrl: String, private val json: Json, private val requestContextHeaders: Map<String, String>, private val requestInterceptor: (suspend (Request.Builder) -> Unit)?, private val responseInterceptor: (suspend (Request, Response) -> Unit)?, private val retry: ${clientName}.RetryPolicy?)`, () => {
|
|
1586
|
+
writer.block(`class ${group.className}(private val client: OkHttpClient, private val baseUrl: String, private val json: Json, private val requestContextHeaders: Map<String, String>, private val requestInterceptor: (suspend (Request.Builder) -> Unit)?, private val responseInterceptor: (suspend (Request, Response) -> Unit)?, private val retry: ${clientName}.RetryPolicy?, private val reconnect: ${clientName}.ReconnectPolicy?)`, () => {
|
|
1549
1587
|
for (const method of group.methods) {
|
|
1550
1588
|
writer.blank();
|
|
1551
1589
|
emitSubClientMethod(writer, method, context);
|
|
@@ -1557,10 +1595,12 @@ const closure = (writer, opener, body) => {
|
|
|
1557
1595
|
writer.indent(body);
|
|
1558
1596
|
writer.line("}");
|
|
1559
1597
|
};
|
|
1560
|
-
const emitKizunaStreaming = (writer) => {
|
|
1598
|
+
const emitKizunaStreaming = (writer, clientName) => {
|
|
1561
1599
|
writer.blank();
|
|
1562
1600
|
writer.line("data class ServerSentEvent(val event: String?, val data: String, val id: String?, val retry: Int?)");
|
|
1563
1601
|
writer.blank();
|
|
1602
|
+
writer.line(`class Reconnection(val policy: ${clientName}.ReconnectPolicy, val reopen: suspend (String) -> Response?)`);
|
|
1603
|
+
writer.blank();
|
|
1564
1604
|
writer.block("private suspend fun readLine(source: okio.BufferedSource): String?", () => {
|
|
1565
1605
|
writer.line("return kotlinx.coroutines.withContext(kotlinx.coroutines.Dispatchers.IO) { source.readUtf8Line() }");
|
|
1566
1606
|
});
|
|
@@ -1587,44 +1627,83 @@ const emitKizunaStreaming = (writer) => {
|
|
|
1587
1627
|
});
|
|
1588
1628
|
});
|
|
1589
1629
|
writer.blank();
|
|
1590
|
-
writer.
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
writer.line("val
|
|
1595
|
-
writer.
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
writer.
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1630
|
+
writer.line("private val LINE_ENDINGS = okio.ByteString.of('\\r'.code.toByte(), '\\n'.code.toByte())");
|
|
1631
|
+
writer.blank();
|
|
1632
|
+
writer.block("private suspend fun readEventLine(source: okio.BufferedSource): String?", () => {
|
|
1633
|
+
closure(writer, "return kotlinx.coroutines.withContext(kotlinx.coroutines.Dispatchers.IO) {", () => {
|
|
1634
|
+
writer.line("val end = source.indexOfElement(LINE_ENDINGS)");
|
|
1635
|
+
writer.block("if (end == -1L)", () => {
|
|
1636
|
+
writer.line("if (source.exhausted()) null else source.readUtf8()");
|
|
1637
|
+
});
|
|
1638
|
+
writer.block("else", () => {
|
|
1639
|
+
writer.line("val line = source.readUtf8(end)");
|
|
1640
|
+
writer.line("val carriageReturn = source.readByte() == '\\r'.code.toByte()");
|
|
1641
|
+
writer.line("if (carriageReturn && source.request(1) && source.buffer[0] == '\\n'.code.toByte()) source.skip(1)");
|
|
1642
|
+
writer.line("line");
|
|
1643
|
+
});
|
|
1644
|
+
});
|
|
1645
|
+
});
|
|
1646
|
+
writer.blank();
|
|
1647
|
+
writer.block("fun serverSentEvents(response: Response, reconnect: Reconnection?): Flow<ServerSentEvent> = flow", () => {
|
|
1648
|
+
writer.line("var lastEventId = \"\"");
|
|
1649
|
+
writer.line("var reconnectionTime = reconnect?.policy?.reconnectionTime ?: 3.seconds");
|
|
1650
|
+
writer.line("var current: Response? = response");
|
|
1651
|
+
writer.line("var attempts = 0");
|
|
1652
|
+
writer.line("var failure: Throwable? = null");
|
|
1653
|
+
writer.block("while (true)", () => {
|
|
1654
|
+
closure(writer, "current?.use { open ->", () => {
|
|
1655
|
+
writer.line("val source = open.body?.source() ?: return@use");
|
|
1656
|
+
writer.line("var eventType = \"\"");
|
|
1657
|
+
writer.line("val dataLines = mutableListOf<String>()");
|
|
1658
|
+
writer.line("var pendingId = lastEventId");
|
|
1659
|
+
writer.line("var retry: Int? = null");
|
|
1660
|
+
writer.line("var firstLine = true");
|
|
1661
|
+
writer.block("while (true)", () => {
|
|
1662
|
+
writer.line("val received = try { readEventLine(source) } catch (error: java.io.IOException) { failure = error; null } ?: break");
|
|
1663
|
+
writer.line("val line = if (firstLine) received.removePrefix(\"\\uFEFF\") else received");
|
|
1664
|
+
writer.line("firstLine = false");
|
|
1665
|
+
writer.block("if (line.isEmpty())", () => {
|
|
1666
|
+
writer.line("lastEventId = pendingId");
|
|
1667
|
+
writer.block("if (dataLines.isNotEmpty())", () => {
|
|
1668
|
+
writer.line("emit(ServerSentEvent(eventType.ifEmpty { null }, dataLines.joinToString(\"\\n\"), lastEventId.ifEmpty { null }, retry))");
|
|
1669
|
+
writer.line("attempts = 0");
|
|
1670
|
+
writer.line("failure = null");
|
|
1671
|
+
writer.line("dataLines.clear()");
|
|
1672
|
+
writer.line("retry = null");
|
|
1673
|
+
});
|
|
1674
|
+
writer.line("eventType = \"\"");
|
|
1675
|
+
writer.line("continue");
|
|
1676
|
+
});
|
|
1677
|
+
writer.line("if (line.startsWith(\":\")) continue");
|
|
1678
|
+
writer.line("val separator = line.indexOf(':')");
|
|
1679
|
+
writer.line("val field = if (separator == -1) line else line.substring(0, separator)");
|
|
1680
|
+
writer.line("val value = (if (separator == -1) \"\" else line.substring(separator + 1)).removePrefix(\" \")");
|
|
1681
|
+
writer.block("when (field)", () => {
|
|
1682
|
+
writer.line("\"event\" -> eventType = value");
|
|
1683
|
+
writer.line("\"data\" -> dataLines.add(value)");
|
|
1684
|
+
writer.line("\"id\" -> if (!value.contains('\\u0000')) pendingId = value");
|
|
1685
|
+
writer.line("\"retry\" -> if (value.isNotEmpty() && value.all { character -> character in '0'..'9' }) value.toIntOrNull()?.let { milliseconds -> retry = milliseconds; reconnectionTime = minOf(milliseconds.milliseconds, 3_600_000.milliseconds) }");
|
|
1604
1686
|
});
|
|
1605
|
-
writer.line("eventType = \"\"");
|
|
1606
|
-
writer.line("continue");
|
|
1607
|
-
});
|
|
1608
|
-
writer.line("if (line.startsWith(\":\")) continue");
|
|
1609
|
-
writer.line("val separator = line.indexOf(':')");
|
|
1610
|
-
writer.line("val field = if (separator == -1) line else line.substring(0, separator)");
|
|
1611
|
-
writer.line("val value = (if (separator == -1) \"\" else line.substring(separator + 1)).removePrefix(\" \")");
|
|
1612
|
-
writer.block("when (field)", () => {
|
|
1613
|
-
writer.line("\"event\" -> eventType = value");
|
|
1614
|
-
writer.line("\"data\" -> dataLines.add(value)");
|
|
1615
|
-
writer.line("\"id\" -> if (!value.contains('\\u0000')) lastEventId = value");
|
|
1616
|
-
writer.line("\"retry\" -> if (value.isNotEmpty() && value.all { it.isDigit() }) retry = value.toIntOrNull()");
|
|
1617
1687
|
});
|
|
1618
1688
|
});
|
|
1689
|
+
writer.block("if (reconnect == null || attempts >= reconnect.policy.maximumAttempts)", () => {
|
|
1690
|
+
writer.line("failure?.let { throw it }");
|
|
1691
|
+
writer.line("return@flow");
|
|
1692
|
+
});
|
|
1693
|
+
writer.line("attempts += 1");
|
|
1694
|
+
writer.line("val ceiling = minOf(reconnect.policy.maximumDelay, reconnectionTime * Math.pow(2.0, (attempts - 1).toDouble()))");
|
|
1695
|
+
writer.line("val extra = (ceiling - reconnectionTime).coerceAtLeast(kotlin.time.Duration.ZERO)");
|
|
1696
|
+
writer.line("kotlinx.coroutines.delay(reconnectionTime + extra * Math.random())");
|
|
1697
|
+
writer.line("current = try { reconnect.reopen(lastEventId) ?: return@flow } catch (error: java.io.IOException) { failure = error; null }");
|
|
1619
1698
|
});
|
|
1620
1699
|
});
|
|
1621
1700
|
writer.blank();
|
|
1622
|
-
writer.line("fun <Event : Any> events(response: Response, decode: (ServerSentEvent) -> Event?): Flow<Event> = serverSentEvents(response).mapNotNull(decode)");
|
|
1701
|
+
writer.line("fun <Event : Any> events(response: Response, reconnect: Reconnection? = null, decode: (ServerSentEvent) -> Event?): Flow<Event> = serverSentEvents(response, reconnect).mapNotNull(decode)");
|
|
1623
1702
|
};
|
|
1624
1703
|
const emitKizunaObject = (writer, options) => {
|
|
1625
1704
|
writer.blank();
|
|
1626
1705
|
writer.block("private object Kizuna", () => {
|
|
1627
|
-
if (options.streaming) emitKizunaStreaming(writer);
|
|
1706
|
+
if (options.streaming) emitKizunaStreaming(writer, options.clientName);
|
|
1628
1707
|
writer.block("fun resolveUrl(baseUrl: String, path: String): HttpUrl.Builder", () => {
|
|
1629
1708
|
writer.line("val base = baseUrl.toHttpUrl()");
|
|
1630
1709
|
writer.line("val trimmedPath = path.trimStart('/')");
|
|
@@ -1721,7 +1800,7 @@ const emitClient = (writer, config, partition, context, typesByOperation, regist
|
|
|
1721
1800
|
const contextRequired = contextFields.some((field) => !hasDefault(field));
|
|
1722
1801
|
const contextParam = contextFields.length > 0 ? `requestContext: RequestContext${contextRequired ? "" : " = RequestContext()"}, ` : "";
|
|
1723
1802
|
writer.blank();
|
|
1724
|
-
writer.block(`class ${clientName}(private val baseUrl: String, ${contextParam}private val client: OkHttpClient = OkHttpClient(), private val json: Json = Json { ignoreUnknownKeys = true }, private val requestInterceptor: (suspend (Request.Builder) -> Unit)? = null, private val responseInterceptor: (suspend (Request, Response) -> Unit)? = null, private val retry: RetryPolicy? = null)`, () => {
|
|
1803
|
+
writer.block(`class ${clientName}(private val baseUrl: String, ${contextParam}private val client: OkHttpClient = OkHttpClient(), private val json: Json = Json { ignoreUnknownKeys = true }, private val requestInterceptor: (suspend (Request.Builder) -> Unit)? = null, private val responseInterceptor: (suspend (Request, Response) -> Unit)? = null, private val retry: RetryPolicy? = null, private val reconnect: ReconnectPolicy? = null)`, () => {
|
|
1725
1804
|
if (contextFields.length > 0) {
|
|
1726
1805
|
writer.blank();
|
|
1727
1806
|
writer.line("/** Values sent as headers on every request, from the api's request context. */");
|
|
@@ -1746,6 +1825,20 @@ const emitClient = (writer, config, partition, context, typesByOperation, regist
|
|
|
1746
1825
|
"val initialDelay: kotlin.time.Duration = 500.milliseconds",
|
|
1747
1826
|
"val maximumDelay: kotlin.time.Duration = 30.seconds"
|
|
1748
1827
|
]);
|
|
1828
|
+
writer.blank();
|
|
1829
|
+
writer.line("/**");
|
|
1830
|
+
writer.line(" * Reconnects an event stream on a GET, HEAD, OPTIONS, PUT or DELETE when its connection drops,");
|
|
1831
|
+
writer.line(" * sending the id of the last event as Last-Event-ID.");
|
|
1832
|
+
writer.line(" *");
|
|
1833
|
+
writer.line(" * @property maximumAttempts Reconnections in a row before the stream fails. An event received resets the count.");
|
|
1834
|
+
writer.line(" * @property reconnectionTime What to wait before reconnecting, until the server sends a retry field.");
|
|
1835
|
+
writer.line(" * @property maximumDelay What the wait may grow to after reconnections in a row that fail.");
|
|
1836
|
+
writer.line(" */");
|
|
1837
|
+
emitConstructorClass(writer, "data class ReconnectPolicy", [
|
|
1838
|
+
"val maximumAttempts: Int = 5",
|
|
1839
|
+
"val reconnectionTime: kotlin.time.Duration = 3.seconds",
|
|
1840
|
+
"val maximumDelay: kotlin.time.Duration = 30.seconds"
|
|
1841
|
+
]);
|
|
1749
1842
|
if (usesMultipart) {
|
|
1750
1843
|
writer.blank();
|
|
1751
1844
|
emitConstructorClass(writer, "data class MultipartFile", [
|
|
@@ -1801,7 +1894,7 @@ const emitClient = (writer, config, partition, context, typesByOperation, regist
|
|
|
1801
1894
|
}
|
|
1802
1895
|
if (groups.length > 0) for (const group of groups) {
|
|
1803
1896
|
writer.blank();
|
|
1804
|
-
writer.line(`val ${escapeKeyword(group.propertyName)} = ${group.className}(client, baseUrl, json, ${contextFields.length > 0 ? "requestContextHeaders" : "emptyMap()"}, requestInterceptor, responseInterceptor, retry)`);
|
|
1897
|
+
writer.line(`val ${escapeKeyword(group.propertyName)} = ${group.className}(client, baseUrl, json, ${contextFields.length > 0 ? "requestContextHeaders" : "emptyMap()"}, requestInterceptor, responseInterceptor, retry, reconnect)`);
|
|
1805
1898
|
}
|
|
1806
1899
|
for (const method of flatMethods) {
|
|
1807
1900
|
writer.blank();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isBinarySchema, isDiscriminatedUnionSchema, isFileSchema, isHintPrefix, isIntegerSchema, isNamedStream, isObjectSchema, isStreamResponse, isSuccessStatus, localTypeName, mergeHeaderFields, parsePath, readDef, readDefType, readDeprecation, readDiscriminatedUnion, readDiscriminatorStringLiteral, readMetaBrand, readMetaDescription, readMetaId, readObjectShape, resolveResponseBody, resolveResponseHeaders, routeStreams, sanitizeFieldName, sanitizeIdentifier, shortTypeName, soleStreamResponse, statusToCamelCase, streamMode, toCamelCase, toPascalCase, unwrapOptionalWrappers, walkApi } from "kizunajs/generator";
|
|
1
|
+
import { isBinarySchema, isDiscriminatedUnionSchema, isFileSchema, isHintPrefix, isIntegerSchema, isJsonMediaType, isNamedStream, isObjectSchema, isStreamResponse, isSuccessStatus, localTypeName, mergeHeaderFields, parsePath, readDef, readDefType, readDeprecation, readDiscriminatedUnion, readDiscriminatorStringLiteral, readMetaBrand, readMetaDescription, readMetaId, readObjectShape, resolveResponseBody, resolveResponseContentType, resolveResponseHeaders, routeStreams, sanitizeFieldName, sanitizeIdentifier, shortTypeName, soleStreamResponse, statusToCamelCase, streamMode, toCamelCase, toPascalCase, unwrapOptionalWrappers, walkApi } from "kizunajs/generator";
|
|
2
2
|
//#region src/emit.ts
|
|
3
3
|
var KotlinWriter = class {
|
|
4
4
|
lines = [];
|
|
@@ -551,18 +551,23 @@ const buildRouteMethod = (routeKey, route, registry, deprecated, deprecationMess
|
|
|
551
551
|
successResponses.push({
|
|
552
552
|
status,
|
|
553
553
|
type: stream.messageType ?? "Event",
|
|
554
|
-
responseHeaders: headersSchema ? collectObjectFields(headersSchema, registry, `${baseHint}ResponseHeaders`) : []
|
|
554
|
+
responseHeaders: headersSchema ? collectObjectFields(headersSchema, registry, `${baseHint}ResponseHeaders`) : [],
|
|
555
|
+
isText: false
|
|
555
556
|
});
|
|
556
557
|
continue;
|
|
557
558
|
}
|
|
558
|
-
const
|
|
559
|
+
const bodySchema = resolveResponseBody(responseValue);
|
|
560
|
+
const responseContentType = resolveResponseContentType(responseValue);
|
|
561
|
+
const isText = !isBinarySchema(bodySchema) && responseContentType !== void 0 && !isJsonMediaType(responseContentType);
|
|
562
|
+
const typeExpression = mapType(bodySchema, registry, responseHint).expression;
|
|
559
563
|
if (isSuccessStatus(status)) {
|
|
560
564
|
const headersSchema = resolveResponseHeaders(responseValue);
|
|
561
565
|
const perStatusHeaderFields = headersSchema ? collectObjectFields(headersSchema, registry, `${baseHint}ResponseHeaders`) : [];
|
|
562
566
|
successResponses.push({
|
|
563
567
|
status,
|
|
564
568
|
type: typeExpression,
|
|
565
|
-
responseHeaders: perStatusHeaderFields
|
|
569
|
+
responseHeaders: perStatusHeaderFields,
|
|
570
|
+
isText
|
|
566
571
|
});
|
|
567
572
|
} else errorCases.push({
|
|
568
573
|
caseName: statusToCamelCase(status),
|
|
@@ -700,9 +705,12 @@ const KOTLIN_PRIMITIVE_TYPES = new Set([
|
|
|
700
705
|
"ByteArray"
|
|
701
706
|
]);
|
|
702
707
|
/**
|
|
703
|
-
* How a response body is read. Raw bytes are the body already.
|
|
708
|
+
* How a response body is read. Raw bytes are the body already, and text is its decoded bytes.
|
|
704
709
|
*/
|
|
705
|
-
const decodeBody = (resolved
|
|
710
|
+
const decodeBody = (resolved, isText = false) => {
|
|
711
|
+
if (resolved === "ByteArray") return "data";
|
|
712
|
+
return isText ? "data.decodeToString()" : `json.decodeFromString<${resolved}>(data.decodeToString())`;
|
|
713
|
+
};
|
|
706
714
|
const resolveType = (typeName, currentOperation, context, scope = "client") => {
|
|
707
715
|
const { operationTypeMap, namespaceName, clientName, fileLevelTypeNames } = context;
|
|
708
716
|
const optional = typeName.endsWith("?");
|
|
@@ -1413,7 +1421,7 @@ const emitMethodBody = (writer, method, context) => {
|
|
|
1413
1421
|
writer.indent(() => {
|
|
1414
1422
|
const resolved = resolveType(successResponse.type, method.operationName, context);
|
|
1415
1423
|
writer.block("try", () => {
|
|
1416
|
-
writer.line(`val payload = ${decodeBody(resolved)}`);
|
|
1424
|
+
writer.line(`val payload = ${decodeBody(resolved, successResponse.isText)}`);
|
|
1417
1425
|
const bodyExpr = isMultiSuccess ? `${operationRef}.Success.Status${successResponse.status}(payload)` : "payload";
|
|
1418
1426
|
if (hasHeaders) {
|
|
1419
1427
|
for (const field of successResponse.responseHeaders) writer.line(`val ${escapeKeyword(field.name)} = httpResponse.header(${stringLiteral(field.wireName)})`);
|
|
@@ -1476,6 +1484,35 @@ const emitErrorBranches = (writer, method, context) => {
|
|
|
1476
1484
|
}
|
|
1477
1485
|
writer.line(`else -> throw ${failureRef}.Unexpected(statusCode = statusCode, data = data)`);
|
|
1478
1486
|
};
|
|
1487
|
+
const IDEMPOTENT_METHODS = new Set([
|
|
1488
|
+
"GET",
|
|
1489
|
+
"HEAD",
|
|
1490
|
+
"OPTIONS",
|
|
1491
|
+
"PUT",
|
|
1492
|
+
"DELETE"
|
|
1493
|
+
]);
|
|
1494
|
+
/**
|
|
1495
|
+
* Writes the reconnection an event stream reopens through and returns the argument handing it to `Kizuna.events`, or
|
|
1496
|
+
* nothing for a method a client may not resend.
|
|
1497
|
+
*/
|
|
1498
|
+
const emitReopen = (writer, method, operationRef, status) => {
|
|
1499
|
+
if (!IDEMPOTENT_METHODS.has(method.method)) return "";
|
|
1500
|
+
closure(writer, "val reconnection = reconnect?.let { policy ->", () => {
|
|
1501
|
+
closure(writer, "Kizuna.Reconnection(policy) { lastEventId ->", () => {
|
|
1502
|
+
writer.line("val again = requestBuilder.build().newBuilder()");
|
|
1503
|
+
writer.line("if (lastEventId.isNotEmpty()) again.header(\"Last-Event-ID\", lastEventId)");
|
|
1504
|
+
writer.line("requestInterceptor?.invoke(again)");
|
|
1505
|
+
writer.line("val reopened = Kizuna.send(client, again.build(), retry)");
|
|
1506
|
+
writer.line("val eventStream = reopened.header(\"Content-Type\")?.lowercase()?.startsWith(\"text/event-stream\") == true");
|
|
1507
|
+
writer.block("when", () => {
|
|
1508
|
+
writer.line("reopened.code == 204 -> { reopened.close(); null }");
|
|
1509
|
+
writer.line(`reopened.code == ${status} && eventStream -> reopened`);
|
|
1510
|
+
writer.line(`else -> { reopened.close(); throw ${operationRef}.Failure.Unexpected(statusCode = reopened.code, data = ByteArray(0)) }`);
|
|
1511
|
+
});
|
|
1512
|
+
});
|
|
1513
|
+
});
|
|
1514
|
+
return ", reconnection";
|
|
1515
|
+
};
|
|
1479
1516
|
const emitStreamMethodTail = (writer, method, context) => {
|
|
1480
1517
|
const operationRef = `${context.clientName}.${method.operationName}`;
|
|
1481
1518
|
const stream = method.stream;
|
|
@@ -1488,7 +1525,7 @@ const emitStreamMethodTail = (writer, method, context) => {
|
|
|
1488
1525
|
else if (stream.events) {
|
|
1489
1526
|
const events = stream.events;
|
|
1490
1527
|
const elementType = streamElementType(method, context, "client");
|
|
1491
|
-
closure(writer, `val body = Kizuna.events<${elementType}>(httpResponse) { event ->`, () => {
|
|
1528
|
+
closure(writer, `val body = Kizuna.events<${elementType}>(httpResponse${emitReopen(writer, method, operationRef, stream.status)}) { event ->`, () => {
|
|
1492
1529
|
writer.block("when (event.event)", () => {
|
|
1493
1530
|
for (const event of events) {
|
|
1494
1531
|
const payloadType = resolveType(event.type, method.operationName, context);
|
|
@@ -1499,7 +1536,8 @@ const emitStreamMethodTail = (writer, method, context) => {
|
|
|
1499
1536
|
});
|
|
1500
1537
|
} else {
|
|
1501
1538
|
const elementType = streamElementType(method, context, "client");
|
|
1502
|
-
|
|
1539
|
+
const reconnection = emitReopen(writer, method, operationRef, stream.status);
|
|
1540
|
+
writer.line(`val body = Kizuna.events<${elementType}>(httpResponse${reconnection}) { event -> json.decodeFromString<${elementType}>(event.data) }`);
|
|
1503
1541
|
}
|
|
1504
1542
|
if (hasHeaders) {
|
|
1505
1543
|
for (const field of successResponse.responseHeaders) writer.line(`val ${escapeKeyword(field.name)} = httpResponse.header(${stringLiteral(field.wireName)})`);
|
|
@@ -1545,7 +1583,7 @@ const emitSubClientMethod = (writer, method, context) => {
|
|
|
1545
1583
|
};
|
|
1546
1584
|
const emitSubClientClass = (writer, group, clientName, context) => {
|
|
1547
1585
|
writer.blank();
|
|
1548
|
-
writer.block(`class ${group.className}(private val client: OkHttpClient, private val baseUrl: String, private val json: Json, private val requestContextHeaders: Map<String, String>, private val requestInterceptor: (suspend (Request.Builder) -> Unit)?, private val responseInterceptor: (suspend (Request, Response) -> Unit)?, private val retry: ${clientName}.RetryPolicy?)`, () => {
|
|
1586
|
+
writer.block(`class ${group.className}(private val client: OkHttpClient, private val baseUrl: String, private val json: Json, private val requestContextHeaders: Map<String, String>, private val requestInterceptor: (suspend (Request.Builder) -> Unit)?, private val responseInterceptor: (suspend (Request, Response) -> Unit)?, private val retry: ${clientName}.RetryPolicy?, private val reconnect: ${clientName}.ReconnectPolicy?)`, () => {
|
|
1549
1587
|
for (const method of group.methods) {
|
|
1550
1588
|
writer.blank();
|
|
1551
1589
|
emitSubClientMethod(writer, method, context);
|
|
@@ -1557,10 +1595,12 @@ const closure = (writer, opener, body) => {
|
|
|
1557
1595
|
writer.indent(body);
|
|
1558
1596
|
writer.line("}");
|
|
1559
1597
|
};
|
|
1560
|
-
const emitKizunaStreaming = (writer) => {
|
|
1598
|
+
const emitKizunaStreaming = (writer, clientName) => {
|
|
1561
1599
|
writer.blank();
|
|
1562
1600
|
writer.line("data class ServerSentEvent(val event: String?, val data: String, val id: String?, val retry: Int?)");
|
|
1563
1601
|
writer.blank();
|
|
1602
|
+
writer.line(`class Reconnection(val policy: ${clientName}.ReconnectPolicy, val reopen: suspend (String) -> Response?)`);
|
|
1603
|
+
writer.blank();
|
|
1564
1604
|
writer.block("private suspend fun readLine(source: okio.BufferedSource): String?", () => {
|
|
1565
1605
|
writer.line("return kotlinx.coroutines.withContext(kotlinx.coroutines.Dispatchers.IO) { source.readUtf8Line() }");
|
|
1566
1606
|
});
|
|
@@ -1587,44 +1627,83 @@ const emitKizunaStreaming = (writer) => {
|
|
|
1587
1627
|
});
|
|
1588
1628
|
});
|
|
1589
1629
|
writer.blank();
|
|
1590
|
-
writer.
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
writer.line("val
|
|
1595
|
-
writer.
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
writer.
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1630
|
+
writer.line("private val LINE_ENDINGS = okio.ByteString.of('\\r'.code.toByte(), '\\n'.code.toByte())");
|
|
1631
|
+
writer.blank();
|
|
1632
|
+
writer.block("private suspend fun readEventLine(source: okio.BufferedSource): String?", () => {
|
|
1633
|
+
closure(writer, "return kotlinx.coroutines.withContext(kotlinx.coroutines.Dispatchers.IO) {", () => {
|
|
1634
|
+
writer.line("val end = source.indexOfElement(LINE_ENDINGS)");
|
|
1635
|
+
writer.block("if (end == -1L)", () => {
|
|
1636
|
+
writer.line("if (source.exhausted()) null else source.readUtf8()");
|
|
1637
|
+
});
|
|
1638
|
+
writer.block("else", () => {
|
|
1639
|
+
writer.line("val line = source.readUtf8(end)");
|
|
1640
|
+
writer.line("val carriageReturn = source.readByte() == '\\r'.code.toByte()");
|
|
1641
|
+
writer.line("if (carriageReturn && source.request(1) && source.buffer[0] == '\\n'.code.toByte()) source.skip(1)");
|
|
1642
|
+
writer.line("line");
|
|
1643
|
+
});
|
|
1644
|
+
});
|
|
1645
|
+
});
|
|
1646
|
+
writer.blank();
|
|
1647
|
+
writer.block("fun serverSentEvents(response: Response, reconnect: Reconnection?): Flow<ServerSentEvent> = flow", () => {
|
|
1648
|
+
writer.line("var lastEventId = \"\"");
|
|
1649
|
+
writer.line("var reconnectionTime = reconnect?.policy?.reconnectionTime ?: 3.seconds");
|
|
1650
|
+
writer.line("var current: Response? = response");
|
|
1651
|
+
writer.line("var attempts = 0");
|
|
1652
|
+
writer.line("var failure: Throwable? = null");
|
|
1653
|
+
writer.block("while (true)", () => {
|
|
1654
|
+
closure(writer, "current?.use { open ->", () => {
|
|
1655
|
+
writer.line("val source = open.body?.source() ?: return@use");
|
|
1656
|
+
writer.line("var eventType = \"\"");
|
|
1657
|
+
writer.line("val dataLines = mutableListOf<String>()");
|
|
1658
|
+
writer.line("var pendingId = lastEventId");
|
|
1659
|
+
writer.line("var retry: Int? = null");
|
|
1660
|
+
writer.line("var firstLine = true");
|
|
1661
|
+
writer.block("while (true)", () => {
|
|
1662
|
+
writer.line("val received = try { readEventLine(source) } catch (error: java.io.IOException) { failure = error; null } ?: break");
|
|
1663
|
+
writer.line("val line = if (firstLine) received.removePrefix(\"\\uFEFF\") else received");
|
|
1664
|
+
writer.line("firstLine = false");
|
|
1665
|
+
writer.block("if (line.isEmpty())", () => {
|
|
1666
|
+
writer.line("lastEventId = pendingId");
|
|
1667
|
+
writer.block("if (dataLines.isNotEmpty())", () => {
|
|
1668
|
+
writer.line("emit(ServerSentEvent(eventType.ifEmpty { null }, dataLines.joinToString(\"\\n\"), lastEventId.ifEmpty { null }, retry))");
|
|
1669
|
+
writer.line("attempts = 0");
|
|
1670
|
+
writer.line("failure = null");
|
|
1671
|
+
writer.line("dataLines.clear()");
|
|
1672
|
+
writer.line("retry = null");
|
|
1673
|
+
});
|
|
1674
|
+
writer.line("eventType = \"\"");
|
|
1675
|
+
writer.line("continue");
|
|
1676
|
+
});
|
|
1677
|
+
writer.line("if (line.startsWith(\":\")) continue");
|
|
1678
|
+
writer.line("val separator = line.indexOf(':')");
|
|
1679
|
+
writer.line("val field = if (separator == -1) line else line.substring(0, separator)");
|
|
1680
|
+
writer.line("val value = (if (separator == -1) \"\" else line.substring(separator + 1)).removePrefix(\" \")");
|
|
1681
|
+
writer.block("when (field)", () => {
|
|
1682
|
+
writer.line("\"event\" -> eventType = value");
|
|
1683
|
+
writer.line("\"data\" -> dataLines.add(value)");
|
|
1684
|
+
writer.line("\"id\" -> if (!value.contains('\\u0000')) pendingId = value");
|
|
1685
|
+
writer.line("\"retry\" -> if (value.isNotEmpty() && value.all { character -> character in '0'..'9' }) value.toIntOrNull()?.let { milliseconds -> retry = milliseconds; reconnectionTime = minOf(milliseconds.milliseconds, 3_600_000.milliseconds) }");
|
|
1604
1686
|
});
|
|
1605
|
-
writer.line("eventType = \"\"");
|
|
1606
|
-
writer.line("continue");
|
|
1607
|
-
});
|
|
1608
|
-
writer.line("if (line.startsWith(\":\")) continue");
|
|
1609
|
-
writer.line("val separator = line.indexOf(':')");
|
|
1610
|
-
writer.line("val field = if (separator == -1) line else line.substring(0, separator)");
|
|
1611
|
-
writer.line("val value = (if (separator == -1) \"\" else line.substring(separator + 1)).removePrefix(\" \")");
|
|
1612
|
-
writer.block("when (field)", () => {
|
|
1613
|
-
writer.line("\"event\" -> eventType = value");
|
|
1614
|
-
writer.line("\"data\" -> dataLines.add(value)");
|
|
1615
|
-
writer.line("\"id\" -> if (!value.contains('\\u0000')) lastEventId = value");
|
|
1616
|
-
writer.line("\"retry\" -> if (value.isNotEmpty() && value.all { it.isDigit() }) retry = value.toIntOrNull()");
|
|
1617
1687
|
});
|
|
1618
1688
|
});
|
|
1689
|
+
writer.block("if (reconnect == null || attempts >= reconnect.policy.maximumAttempts)", () => {
|
|
1690
|
+
writer.line("failure?.let { throw it }");
|
|
1691
|
+
writer.line("return@flow");
|
|
1692
|
+
});
|
|
1693
|
+
writer.line("attempts += 1");
|
|
1694
|
+
writer.line("val ceiling = minOf(reconnect.policy.maximumDelay, reconnectionTime * Math.pow(2.0, (attempts - 1).toDouble()))");
|
|
1695
|
+
writer.line("val extra = (ceiling - reconnectionTime).coerceAtLeast(kotlin.time.Duration.ZERO)");
|
|
1696
|
+
writer.line("kotlinx.coroutines.delay(reconnectionTime + extra * Math.random())");
|
|
1697
|
+
writer.line("current = try { reconnect.reopen(lastEventId) ?: return@flow } catch (error: java.io.IOException) { failure = error; null }");
|
|
1619
1698
|
});
|
|
1620
1699
|
});
|
|
1621
1700
|
writer.blank();
|
|
1622
|
-
writer.line("fun <Event : Any> events(response: Response, decode: (ServerSentEvent) -> Event?): Flow<Event> = serverSentEvents(response).mapNotNull(decode)");
|
|
1701
|
+
writer.line("fun <Event : Any> events(response: Response, reconnect: Reconnection? = null, decode: (ServerSentEvent) -> Event?): Flow<Event> = serverSentEvents(response, reconnect).mapNotNull(decode)");
|
|
1623
1702
|
};
|
|
1624
1703
|
const emitKizunaObject = (writer, options) => {
|
|
1625
1704
|
writer.blank();
|
|
1626
1705
|
writer.block("private object Kizuna", () => {
|
|
1627
|
-
if (options.streaming) emitKizunaStreaming(writer);
|
|
1706
|
+
if (options.streaming) emitKizunaStreaming(writer, options.clientName);
|
|
1628
1707
|
writer.block("fun resolveUrl(baseUrl: String, path: String): HttpUrl.Builder", () => {
|
|
1629
1708
|
writer.line("val base = baseUrl.toHttpUrl()");
|
|
1630
1709
|
writer.line("val trimmedPath = path.trimStart('/')");
|
|
@@ -1721,7 +1800,7 @@ const emitClient = (writer, config, partition, context, typesByOperation, regist
|
|
|
1721
1800
|
const contextRequired = contextFields.some((field) => !hasDefault(field));
|
|
1722
1801
|
const contextParam = contextFields.length > 0 ? `requestContext: RequestContext${contextRequired ? "" : " = RequestContext()"}, ` : "";
|
|
1723
1802
|
writer.blank();
|
|
1724
|
-
writer.block(`class ${clientName}(private val baseUrl: String, ${contextParam}private val client: OkHttpClient = OkHttpClient(), private val json: Json = Json { ignoreUnknownKeys = true }, private val requestInterceptor: (suspend (Request.Builder) -> Unit)? = null, private val responseInterceptor: (suspend (Request, Response) -> Unit)? = null, private val retry: RetryPolicy? = null)`, () => {
|
|
1803
|
+
writer.block(`class ${clientName}(private val baseUrl: String, ${contextParam}private val client: OkHttpClient = OkHttpClient(), private val json: Json = Json { ignoreUnknownKeys = true }, private val requestInterceptor: (suspend (Request.Builder) -> Unit)? = null, private val responseInterceptor: (suspend (Request, Response) -> Unit)? = null, private val retry: RetryPolicy? = null, private val reconnect: ReconnectPolicy? = null)`, () => {
|
|
1725
1804
|
if (contextFields.length > 0) {
|
|
1726
1805
|
writer.blank();
|
|
1727
1806
|
writer.line("/** Values sent as headers on every request, from the api's request context. */");
|
|
@@ -1746,6 +1825,20 @@ const emitClient = (writer, config, partition, context, typesByOperation, regist
|
|
|
1746
1825
|
"val initialDelay: kotlin.time.Duration = 500.milliseconds",
|
|
1747
1826
|
"val maximumDelay: kotlin.time.Duration = 30.seconds"
|
|
1748
1827
|
]);
|
|
1828
|
+
writer.blank();
|
|
1829
|
+
writer.line("/**");
|
|
1830
|
+
writer.line(" * Reconnects an event stream on a GET, HEAD, OPTIONS, PUT or DELETE when its connection drops,");
|
|
1831
|
+
writer.line(" * sending the id of the last event as Last-Event-ID.");
|
|
1832
|
+
writer.line(" *");
|
|
1833
|
+
writer.line(" * @property maximumAttempts Reconnections in a row before the stream fails. An event received resets the count.");
|
|
1834
|
+
writer.line(" * @property reconnectionTime What to wait before reconnecting, until the server sends a retry field.");
|
|
1835
|
+
writer.line(" * @property maximumDelay What the wait may grow to after reconnections in a row that fail.");
|
|
1836
|
+
writer.line(" */");
|
|
1837
|
+
emitConstructorClass(writer, "data class ReconnectPolicy", [
|
|
1838
|
+
"val maximumAttempts: Int = 5",
|
|
1839
|
+
"val reconnectionTime: kotlin.time.Duration = 3.seconds",
|
|
1840
|
+
"val maximumDelay: kotlin.time.Duration = 30.seconds"
|
|
1841
|
+
]);
|
|
1749
1842
|
if (usesMultipart) {
|
|
1750
1843
|
writer.blank();
|
|
1751
1844
|
emitConstructorClass(writer, "data class MultipartFile", [
|
|
@@ -1801,7 +1894,7 @@ const emitClient = (writer, config, partition, context, typesByOperation, regist
|
|
|
1801
1894
|
}
|
|
1802
1895
|
if (groups.length > 0) for (const group of groups) {
|
|
1803
1896
|
writer.blank();
|
|
1804
|
-
writer.line(`val ${escapeKeyword(group.propertyName)} = ${group.className}(client, baseUrl, json, ${contextFields.length > 0 ? "requestContextHeaders" : "emptyMap()"}, requestInterceptor, responseInterceptor, retry)`);
|
|
1897
|
+
writer.line(`val ${escapeKeyword(group.propertyName)} = ${group.className}(client, baseUrl, json, ${contextFields.length > 0 ? "requestContextHeaders" : "emptyMap()"}, requestInterceptor, responseInterceptor, retry, reconnect)`);
|
|
1805
1898
|
}
|
|
1806
1899
|
for (const method of flatMethods) {
|
|
1807
1900
|
writer.blank();
|
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_generator = require("./generator-
|
|
2
|
+
const require_generator = require("./generator-DGtAlheu.cjs");
|
|
3
3
|
let kizunajs_generator = require("kizunajs/generator");
|
|
4
4
|
let zod = require("zod");
|
|
5
5
|
//#region src/client-target.ts
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as kotlinClientWalk, t as generateKotlinClient } from "./generator-
|
|
1
|
+
import { n as kotlinClientWalk, t as generateKotlinClient } from "./generator-oVr_SgXs.mjs";
|
|
2
2
|
import { defineClient } from "kizunajs/generator";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kizunajs/kotlin",
|
|
3
|
-
"version": "2.0.0-beta.
|
|
3
|
+
"version": "2.0.0-beta.22",
|
|
4
4
|
"description": "Kotlin client generation for Kizuna.js APIs",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"kizunajs",
|
|
@@ -60,14 +60,14 @@
|
|
|
60
60
|
"access": "public"
|
|
61
61
|
},
|
|
62
62
|
"dependencies": {
|
|
63
|
-
"@kizunajs/cli": "2.0.0-beta.
|
|
63
|
+
"@kizunajs/cli": "2.0.0-beta.22"
|
|
64
64
|
},
|
|
65
65
|
"peerDependencies": {
|
|
66
|
-
"kizunajs": "2.0.0-beta.
|
|
66
|
+
"kizunajs": "2.0.0-beta.22",
|
|
67
67
|
"zod": "^4.0.0"
|
|
68
68
|
},
|
|
69
69
|
"devDependencies": {
|
|
70
|
-
"kizunajs": "2.0.0-beta.
|
|
70
|
+
"kizunajs": "2.0.0-beta.22",
|
|
71
71
|
"@types/node": "^22.9.0",
|
|
72
72
|
"tsdown": "^0.21.0",
|
|
73
73
|
"typescript": "^6.0.3",
|