@kizunajs/kotlin 2.0.0-beta.1 → 2.0.0-beta.11
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/README.md +18 -1
- package/dist/cli.cjs +1 -1
- package/dist/cli.mjs +1 -1
- package/dist/{generator-s2pn7vqP.cjs → generator-Cs9NkTCg.cjs} +58 -28
- package/dist/{generator-BvNpSarY.mjs → generator-uzHpr6im.mjs} +54 -30
- package/dist/index.cjs +39 -8
- package/dist/index.d.cts +22 -34
- package/dist/index.d.mts +22 -34
- package/dist/index.mjs +38 -9
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -10,8 +10,25 @@ pnpm add -D @kizunajs/kotlin
|
|
|
10
10
|
|
|
11
11
|
## Usage
|
|
12
12
|
|
|
13
|
+
Name the client in your config, and `kizuna generate` writes it with the rest:
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { kotlinClient } from '@kizunajs/kotlin';
|
|
17
|
+
|
|
18
|
+
export default defineConfig({
|
|
19
|
+
routes,
|
|
20
|
+
clients: [
|
|
21
|
+
kotlinClient({
|
|
22
|
+
output: '../android/app/src/main/kotlin/com/example/APIClient.kt',
|
|
23
|
+
namespace: 'API',
|
|
24
|
+
package: 'com.example',
|
|
25
|
+
}),
|
|
26
|
+
],
|
|
27
|
+
});
|
|
28
|
+
```
|
|
29
|
+
|
|
13
30
|
```sh
|
|
14
|
-
kizuna
|
|
31
|
+
kizuna generate
|
|
15
32
|
```
|
|
16
33
|
|
|
17
34
|
## Documentation
|
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-uzHpr6im.mjs";
|
|
3
3
|
import { mkdirSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { dirname, resolve } from "node:path";
|
|
5
5
|
import { parseArgs } from "node:util";
|
|
@@ -117,8 +117,7 @@ const propertyName = (key, camelCase) => {
|
|
|
117
117
|
if (!camel) return "field";
|
|
118
118
|
return /^[0-9]/.test(camel) ? `_${camel}` : camel;
|
|
119
119
|
};
|
|
120
|
-
const objectFields = (schema, registry, hint) => {
|
|
121
|
-
const shape = (0, kizunajs_generator.readObjectShape)(schema) ?? {};
|
|
120
|
+
const objectFields = (schema, registry, hint, shape = (0, kizunajs_generator.readObjectShape)(schema) ?? {}) => {
|
|
122
121
|
const fields = [];
|
|
123
122
|
const seen = /* @__PURE__ */ new Map();
|
|
124
123
|
for (const [key, value] of Object.entries(shape)) {
|
|
@@ -148,6 +147,10 @@ const mapType = (schema, registry, hint) => {
|
|
|
148
147
|
optional: false,
|
|
149
148
|
isFile: true
|
|
150
149
|
};
|
|
150
|
+
if ((0, kizunajs_generator.isBinarySchema)(schema)) return {
|
|
151
|
+
expression: "ByteArray",
|
|
152
|
+
optional: false
|
|
153
|
+
};
|
|
151
154
|
const id = (0, kizunajs_generator.readMetaId)(schema);
|
|
152
155
|
const def = (0, kizunajs_generator.readDef)(schema);
|
|
153
156
|
const discriminated = (0, kizunajs_generator.readDiscriminatedUnion)(schema);
|
|
@@ -223,8 +226,8 @@ const mapType = (schema, registry, hint) => {
|
|
|
223
226
|
optional: false
|
|
224
227
|
};
|
|
225
228
|
}
|
|
226
|
-
const { inner, optional } = (0, kizunajs_generator.unwrapOptionalWrappers)(schema);
|
|
227
|
-
if (optional) {
|
|
229
|
+
const { inner, optional, nullable } = (0, kizunajs_generator.unwrapOptionalWrappers)(schema);
|
|
230
|
+
if (optional || nullable) {
|
|
228
231
|
const innerResult = mapType(inner, registry, hint);
|
|
229
232
|
return {
|
|
230
233
|
expression: innerResult.expression.endsWith("?") ? innerResult.expression : `${innerResult.expression}?`,
|
|
@@ -277,8 +280,9 @@ const mapType = (schema, registry, hint) => {
|
|
|
277
280
|
optional: false
|
|
278
281
|
};
|
|
279
282
|
}
|
|
283
|
+
const elementType = mapType(element, registry, `${hint}Item`).expression.replace(/\?$/, "");
|
|
280
284
|
return {
|
|
281
|
-
expression: `List<${
|
|
285
|
+
expression: `List<${(0, kizunajs_generator.unwrapOptionalWrappers)(element).nullable ? `${elementType}?` : elementType}>`,
|
|
282
286
|
optional: false
|
|
283
287
|
};
|
|
284
288
|
}
|
|
@@ -576,7 +580,7 @@ const buildRouteMethod = (routeKey, route, registry, deprecated, deprecationMess
|
|
|
576
580
|
stream
|
|
577
581
|
};
|
|
578
582
|
};
|
|
579
|
-
const
|
|
583
|
+
const kotlinRoutesWalk = (options) => {
|
|
580
584
|
const flatMethods = [];
|
|
581
585
|
const groupMap = /* @__PURE__ */ new Map();
|
|
582
586
|
return {
|
|
@@ -608,7 +612,7 @@ const kotlinGenerator = (0, kizunajs_generator.createGenerator)((options, _api)
|
|
|
608
612
|
};
|
|
609
613
|
}
|
|
610
614
|
};
|
|
611
|
-
}
|
|
615
|
+
};
|
|
612
616
|
const KOTLIN_KEYWORDS = new Set([
|
|
613
617
|
"as",
|
|
614
618
|
"break",
|
|
@@ -653,8 +657,13 @@ const KOTLIN_PRIMITIVE_TYPES = new Set([
|
|
|
653
657
|
"Long",
|
|
654
658
|
"Boolean",
|
|
655
659
|
"Instant",
|
|
656
|
-
"Unit"
|
|
660
|
+
"Unit",
|
|
661
|
+
"ByteArray"
|
|
657
662
|
]);
|
|
663
|
+
/**
|
|
664
|
+
* How a response body is read. Raw bytes are the body already.
|
|
665
|
+
*/
|
|
666
|
+
const decodeBody = (resolved) => resolved === "ByteArray" ? "data" : `json.decodeFromString<${resolved}>(data.decodeToString())`;
|
|
658
667
|
const resolveType = (typeName, currentOperation, context, scope = "client") => {
|
|
659
668
|
const { operationTypeMap, namespaceName, clientName, fileLevelTypeNames } = context;
|
|
660
669
|
const optional = typeName.endsWith("?");
|
|
@@ -1002,7 +1011,7 @@ const emitOperationResultTypes = (writer, method, context) => {
|
|
|
1002
1011
|
writer.line(") {");
|
|
1003
1012
|
writer.indent(() => {
|
|
1004
1013
|
writer.line("/** How far along the call is. */");
|
|
1005
|
-
writer.line("enum class State { Running, Done, Failed }");
|
|
1014
|
+
writer.line("enum class State { Running, NeedsApproval, Done, Failed }");
|
|
1006
1015
|
});
|
|
1007
1016
|
writer.line("}");
|
|
1008
1017
|
writer.blank();
|
|
@@ -1012,7 +1021,12 @@ const emitOperationResultTypes = (writer, method, context) => {
|
|
|
1012
1021
|
writer.block("for (event in events)", () => {
|
|
1013
1022
|
writer.line("when (event) {");
|
|
1014
1023
|
writer.indent(() => {
|
|
1015
|
-
writer.line("is Event.ToolCall ->
|
|
1024
|
+
writer.line("is Event.ToolCall -> {");
|
|
1025
|
+
writer.indent(() => {
|
|
1026
|
+
writer.line("val tracked = (calls[event.data.id] ?: ToolCallRecord(event.data.id, event.data.name)).copy(call = event.data)");
|
|
1027
|
+
writer.line("calls[event.data.id] = if (event.data.needsApproval == true && tracked.state == ToolCallRecord.State.Running) tracked.copy(state = ToolCallRecord.State.NeedsApproval) else tracked");
|
|
1028
|
+
});
|
|
1029
|
+
writer.line("}");
|
|
1016
1030
|
writer.line("is Event.ToolResult -> calls[event.data.id] = (calls[event.data.id] ?: ToolCallRecord(event.data.id, event.data.name)).copy(state = ToolCallRecord.State.Done, result = event.data)");
|
|
1017
1031
|
if (eventNames.has("tool_error")) writer.line("is Event.ToolError -> calls[event.data.id] = (calls[event.data.id] ?: ToolCallRecord(event.data.id, event.data.name.wireValue)).copy(state = ToolCallRecord.State.Failed, message = event.data.message)");
|
|
1018
1032
|
writer.line("else -> Unit");
|
|
@@ -1344,7 +1358,7 @@ const emitMethodBody = (writer, method, context) => {
|
|
|
1344
1358
|
writer.indent(() => {
|
|
1345
1359
|
const resolved = resolveType(successResponse.type, method.operationName, context);
|
|
1346
1360
|
writer.block("try", () => {
|
|
1347
|
-
writer.line(`val payload =
|
|
1361
|
+
writer.line(`val payload = ${decodeBody(resolved)}`);
|
|
1348
1362
|
const bodyExpr = isMultiSuccess ? `${operationRef}.Success.Status${successResponse.status}(payload)` : "payload";
|
|
1349
1363
|
if (hasHeaders) {
|
|
1350
1364
|
for (const field of successResponse.responseHeaders) writer.line(`val ${escapeKeyword(field.name)} = httpResponse.header(${stringLiteral(field.wireName)})`);
|
|
@@ -1381,7 +1395,7 @@ const emitErrorBranches = (writer, method, context) => {
|
|
|
1381
1395
|
const resolved = resolveType(errorCase.type, method.operationName, context);
|
|
1382
1396
|
writer.line("val payload = try {");
|
|
1383
1397
|
writer.indent(() => {
|
|
1384
|
-
writer.line(
|
|
1398
|
+
writer.line(decodeBody(resolved));
|
|
1385
1399
|
});
|
|
1386
1400
|
writer.line(`} catch (error: Exception) { throw ${failureRef}.Decoding(error, statusCode, data) }`);
|
|
1387
1401
|
writer.line(`throw ${failureRef}.${caseName}(body = payload)`);
|
|
@@ -1394,7 +1408,7 @@ const emitErrorBranches = (writer, method, context) => {
|
|
|
1394
1408
|
const resolved = resolveType(errorCase.type, method.operationName, context);
|
|
1395
1409
|
writer.line(`val ${errorCase.caseName} = try {`);
|
|
1396
1410
|
writer.indent(() => {
|
|
1397
|
-
writer.line(
|
|
1411
|
+
writer.line(decodeBody(resolved));
|
|
1398
1412
|
});
|
|
1399
1413
|
writer.line("} catch (_: Exception) { null }");
|
|
1400
1414
|
writer.line(`if (${errorCase.caseName} != null) throw ${failureRef}.${caseName}(body = ${errorCase.caseName})`);
|
|
@@ -1685,21 +1699,8 @@ const emitClient = (writer, config, partition, context, typesByOperation, regist
|
|
|
1685
1699
|
}
|
|
1686
1700
|
});
|
|
1687
1701
|
};
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
*
|
|
1691
|
-
* @param api - What `defineConfig` assembled.
|
|
1692
|
-
* @param config - Override the generated names:
|
|
1693
|
-
* - `namespaceName`: the object wrapping shared types.
|
|
1694
|
-
* - `packageName`: optional package declaration for the generated file.
|
|
1695
|
-
*/
|
|
1696
|
-
const generateKotlinClient = (api, config) => {
|
|
1697
|
-
const { namespaceName, packageName, camelCaseProperties = false, unknownEnumCase = false } = config;
|
|
1698
|
-
const registry = new TypeRegistry(camelCaseProperties, unknownEnumCase);
|
|
1699
|
-
const partition = kotlinGenerator(api, {
|
|
1700
|
-
namespaceName,
|
|
1701
|
-
registry
|
|
1702
|
-
});
|
|
1702
|
+
const renderKotlinClient = (api, partition, registry, config) => {
|
|
1703
|
+
const { namespaceName, packageName, camelCaseProperties = false } = config;
|
|
1703
1704
|
const allMethods = [...partition.flatMethods, ...partition.groups.flatMap((group) => group.methods)];
|
|
1704
1705
|
const operationTypeMap = buildOperationTypeMap(allMethods, registry);
|
|
1705
1706
|
const clientName = `${namespaceName}Client`;
|
|
@@ -1791,6 +1792,29 @@ const generateKotlinClient = (api, config) => {
|
|
|
1791
1792
|
for (const warning of registry.warnings()) process.stderr.write(`[kizuna/kotlin] JsonElement fallback at ${warning}\n`);
|
|
1792
1793
|
return writer.toString();
|
|
1793
1794
|
};
|
|
1795
|
+
/**
|
|
1796
|
+
* The Kotlin client's walk: the routes into a partition, then the file.
|
|
1797
|
+
*/
|
|
1798
|
+
const kotlinClientWalk = (api, config) => {
|
|
1799
|
+
const registry = new TypeRegistry(config.camelCaseProperties ?? false, config.unknownEnumCase ?? false);
|
|
1800
|
+
const routes = kotlinRoutesWalk({
|
|
1801
|
+
namespaceName: config.namespaceName,
|
|
1802
|
+
registry
|
|
1803
|
+
});
|
|
1804
|
+
return {
|
|
1805
|
+
processRoute: routes.processRoute,
|
|
1806
|
+
finalize: () => renderKotlinClient(api, routes.finalize(), registry, config)
|
|
1807
|
+
};
|
|
1808
|
+
};
|
|
1809
|
+
/**
|
|
1810
|
+
* Generate a Kotlin API client from a Kizuna api.
|
|
1811
|
+
*
|
|
1812
|
+
* @param api - What `defineConfig` assembled.
|
|
1813
|
+
* @param config - Override the generated names:
|
|
1814
|
+
* - `namespaceName`: the object wrapping shared types.
|
|
1815
|
+
* - `packageName`: optional package declaration for the generated file.
|
|
1816
|
+
*/
|
|
1817
|
+
const generateKotlinClient = (api, config) => (0, kizunajs_generator.walkApi)(api, kotlinClientWalk(api, config), { skipHidden: true });
|
|
1794
1818
|
//#endregion
|
|
1795
1819
|
Object.defineProperty(exports, "generateKotlinClient", {
|
|
1796
1820
|
enumerable: true,
|
|
@@ -1798,3 +1822,9 @@ Object.defineProperty(exports, "generateKotlinClient", {
|
|
|
1798
1822
|
return generateKotlinClient;
|
|
1799
1823
|
}
|
|
1800
1824
|
});
|
|
1825
|
+
Object.defineProperty(exports, "kotlinClientWalk", {
|
|
1826
|
+
enumerable: true,
|
|
1827
|
+
get: function() {
|
|
1828
|
+
return kotlinClientWalk;
|
|
1829
|
+
}
|
|
1830
|
+
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isBinarySchema, isDiscriminatedUnionSchema, isFileSchema, isHintPrefix, isIntegerSchema, isNamedStream, isObjectSchema, isStreamResponse, isSuccessStatus, localTypeName, mergeHeaderFields, parsePath, readDef, readDefType, readDeprecation, readDiscriminatedUnion, readDiscriminatorStringLiteral, readMetaDescription, readMetaId, readObjectShape, resolveResponseBody, 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 = [];
|
|
@@ -117,8 +117,7 @@ const propertyName = (key, camelCase) => {
|
|
|
117
117
|
if (!camel) return "field";
|
|
118
118
|
return /^[0-9]/.test(camel) ? `_${camel}` : camel;
|
|
119
119
|
};
|
|
120
|
-
const objectFields = (schema, registry, hint) => {
|
|
121
|
-
const shape = readObjectShape(schema) ?? {};
|
|
120
|
+
const objectFields = (schema, registry, hint, shape = readObjectShape(schema) ?? {}) => {
|
|
122
121
|
const fields = [];
|
|
123
122
|
const seen = /* @__PURE__ */ new Map();
|
|
124
123
|
for (const [key, value] of Object.entries(shape)) {
|
|
@@ -148,6 +147,10 @@ const mapType = (schema, registry, hint) => {
|
|
|
148
147
|
optional: false,
|
|
149
148
|
isFile: true
|
|
150
149
|
};
|
|
150
|
+
if (isBinarySchema(schema)) return {
|
|
151
|
+
expression: "ByteArray",
|
|
152
|
+
optional: false
|
|
153
|
+
};
|
|
151
154
|
const id = readMetaId(schema);
|
|
152
155
|
const def = readDef(schema);
|
|
153
156
|
const discriminated = readDiscriminatedUnion(schema);
|
|
@@ -223,8 +226,8 @@ const mapType = (schema, registry, hint) => {
|
|
|
223
226
|
optional: false
|
|
224
227
|
};
|
|
225
228
|
}
|
|
226
|
-
const { inner, optional } = unwrapOptionalWrappers(schema);
|
|
227
|
-
if (optional) {
|
|
229
|
+
const { inner, optional, nullable } = unwrapOptionalWrappers(schema);
|
|
230
|
+
if (optional || nullable) {
|
|
228
231
|
const innerResult = mapType(inner, registry, hint);
|
|
229
232
|
return {
|
|
230
233
|
expression: innerResult.expression.endsWith("?") ? innerResult.expression : `${innerResult.expression}?`,
|
|
@@ -277,8 +280,9 @@ const mapType = (schema, registry, hint) => {
|
|
|
277
280
|
optional: false
|
|
278
281
|
};
|
|
279
282
|
}
|
|
283
|
+
const elementType = mapType(element, registry, `${hint}Item`).expression.replace(/\?$/, "");
|
|
280
284
|
return {
|
|
281
|
-
expression: `List<${
|
|
285
|
+
expression: `List<${unwrapOptionalWrappers(element).nullable ? `${elementType}?` : elementType}>`,
|
|
282
286
|
optional: false
|
|
283
287
|
};
|
|
284
288
|
}
|
|
@@ -576,7 +580,7 @@ const buildRouteMethod = (routeKey, route, registry, deprecated, deprecationMess
|
|
|
576
580
|
stream
|
|
577
581
|
};
|
|
578
582
|
};
|
|
579
|
-
const
|
|
583
|
+
const kotlinRoutesWalk = (options) => {
|
|
580
584
|
const flatMethods = [];
|
|
581
585
|
const groupMap = /* @__PURE__ */ new Map();
|
|
582
586
|
return {
|
|
@@ -608,7 +612,7 @@ const kotlinGenerator = createGenerator((options, _api) => {
|
|
|
608
612
|
};
|
|
609
613
|
}
|
|
610
614
|
};
|
|
611
|
-
}
|
|
615
|
+
};
|
|
612
616
|
const KOTLIN_KEYWORDS = new Set([
|
|
613
617
|
"as",
|
|
614
618
|
"break",
|
|
@@ -653,8 +657,13 @@ const KOTLIN_PRIMITIVE_TYPES = new Set([
|
|
|
653
657
|
"Long",
|
|
654
658
|
"Boolean",
|
|
655
659
|
"Instant",
|
|
656
|
-
"Unit"
|
|
660
|
+
"Unit",
|
|
661
|
+
"ByteArray"
|
|
657
662
|
]);
|
|
663
|
+
/**
|
|
664
|
+
* How a response body is read. Raw bytes are the body already.
|
|
665
|
+
*/
|
|
666
|
+
const decodeBody = (resolved) => resolved === "ByteArray" ? "data" : `json.decodeFromString<${resolved}>(data.decodeToString())`;
|
|
658
667
|
const resolveType = (typeName, currentOperation, context, scope = "client") => {
|
|
659
668
|
const { operationTypeMap, namespaceName, clientName, fileLevelTypeNames } = context;
|
|
660
669
|
const optional = typeName.endsWith("?");
|
|
@@ -1002,7 +1011,7 @@ const emitOperationResultTypes = (writer, method, context) => {
|
|
|
1002
1011
|
writer.line(") {");
|
|
1003
1012
|
writer.indent(() => {
|
|
1004
1013
|
writer.line("/** How far along the call is. */");
|
|
1005
|
-
writer.line("enum class State { Running, Done, Failed }");
|
|
1014
|
+
writer.line("enum class State { Running, NeedsApproval, Done, Failed }");
|
|
1006
1015
|
});
|
|
1007
1016
|
writer.line("}");
|
|
1008
1017
|
writer.blank();
|
|
@@ -1012,7 +1021,12 @@ const emitOperationResultTypes = (writer, method, context) => {
|
|
|
1012
1021
|
writer.block("for (event in events)", () => {
|
|
1013
1022
|
writer.line("when (event) {");
|
|
1014
1023
|
writer.indent(() => {
|
|
1015
|
-
writer.line("is Event.ToolCall ->
|
|
1024
|
+
writer.line("is Event.ToolCall -> {");
|
|
1025
|
+
writer.indent(() => {
|
|
1026
|
+
writer.line("val tracked = (calls[event.data.id] ?: ToolCallRecord(event.data.id, event.data.name)).copy(call = event.data)");
|
|
1027
|
+
writer.line("calls[event.data.id] = if (event.data.needsApproval == true && tracked.state == ToolCallRecord.State.Running) tracked.copy(state = ToolCallRecord.State.NeedsApproval) else tracked");
|
|
1028
|
+
});
|
|
1029
|
+
writer.line("}");
|
|
1016
1030
|
writer.line("is Event.ToolResult -> calls[event.data.id] = (calls[event.data.id] ?: ToolCallRecord(event.data.id, event.data.name)).copy(state = ToolCallRecord.State.Done, result = event.data)");
|
|
1017
1031
|
if (eventNames.has("tool_error")) writer.line("is Event.ToolError -> calls[event.data.id] = (calls[event.data.id] ?: ToolCallRecord(event.data.id, event.data.name.wireValue)).copy(state = ToolCallRecord.State.Failed, message = event.data.message)");
|
|
1018
1032
|
writer.line("else -> Unit");
|
|
@@ -1344,7 +1358,7 @@ const emitMethodBody = (writer, method, context) => {
|
|
|
1344
1358
|
writer.indent(() => {
|
|
1345
1359
|
const resolved = resolveType(successResponse.type, method.operationName, context);
|
|
1346
1360
|
writer.block("try", () => {
|
|
1347
|
-
writer.line(`val payload =
|
|
1361
|
+
writer.line(`val payload = ${decodeBody(resolved)}`);
|
|
1348
1362
|
const bodyExpr = isMultiSuccess ? `${operationRef}.Success.Status${successResponse.status}(payload)` : "payload";
|
|
1349
1363
|
if (hasHeaders) {
|
|
1350
1364
|
for (const field of successResponse.responseHeaders) writer.line(`val ${escapeKeyword(field.name)} = httpResponse.header(${stringLiteral(field.wireName)})`);
|
|
@@ -1381,7 +1395,7 @@ const emitErrorBranches = (writer, method, context) => {
|
|
|
1381
1395
|
const resolved = resolveType(errorCase.type, method.operationName, context);
|
|
1382
1396
|
writer.line("val payload = try {");
|
|
1383
1397
|
writer.indent(() => {
|
|
1384
|
-
writer.line(
|
|
1398
|
+
writer.line(decodeBody(resolved));
|
|
1385
1399
|
});
|
|
1386
1400
|
writer.line(`} catch (error: Exception) { throw ${failureRef}.Decoding(error, statusCode, data) }`);
|
|
1387
1401
|
writer.line(`throw ${failureRef}.${caseName}(body = payload)`);
|
|
@@ -1394,7 +1408,7 @@ const emitErrorBranches = (writer, method, context) => {
|
|
|
1394
1408
|
const resolved = resolveType(errorCase.type, method.operationName, context);
|
|
1395
1409
|
writer.line(`val ${errorCase.caseName} = try {`);
|
|
1396
1410
|
writer.indent(() => {
|
|
1397
|
-
writer.line(
|
|
1411
|
+
writer.line(decodeBody(resolved));
|
|
1398
1412
|
});
|
|
1399
1413
|
writer.line("} catch (_: Exception) { null }");
|
|
1400
1414
|
writer.line(`if (${errorCase.caseName} != null) throw ${failureRef}.${caseName}(body = ${errorCase.caseName})`);
|
|
@@ -1685,21 +1699,8 @@ const emitClient = (writer, config, partition, context, typesByOperation, regist
|
|
|
1685
1699
|
}
|
|
1686
1700
|
});
|
|
1687
1701
|
};
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
*
|
|
1691
|
-
* @param api - What `defineConfig` assembled.
|
|
1692
|
-
* @param config - Override the generated names:
|
|
1693
|
-
* - `namespaceName`: the object wrapping shared types.
|
|
1694
|
-
* - `packageName`: optional package declaration for the generated file.
|
|
1695
|
-
*/
|
|
1696
|
-
const generateKotlinClient = (api, config) => {
|
|
1697
|
-
const { namespaceName, packageName, camelCaseProperties = false, unknownEnumCase = false } = config;
|
|
1698
|
-
const registry = new TypeRegistry(camelCaseProperties, unknownEnumCase);
|
|
1699
|
-
const partition = kotlinGenerator(api, {
|
|
1700
|
-
namespaceName,
|
|
1701
|
-
registry
|
|
1702
|
-
});
|
|
1702
|
+
const renderKotlinClient = (api, partition, registry, config) => {
|
|
1703
|
+
const { namespaceName, packageName, camelCaseProperties = false } = config;
|
|
1703
1704
|
const allMethods = [...partition.flatMethods, ...partition.groups.flatMap((group) => group.methods)];
|
|
1704
1705
|
const operationTypeMap = buildOperationTypeMap(allMethods, registry);
|
|
1705
1706
|
const clientName = `${namespaceName}Client`;
|
|
@@ -1791,5 +1792,28 @@ const generateKotlinClient = (api, config) => {
|
|
|
1791
1792
|
for (const warning of registry.warnings()) process.stderr.write(`[kizuna/kotlin] JsonElement fallback at ${warning}\n`);
|
|
1792
1793
|
return writer.toString();
|
|
1793
1794
|
};
|
|
1795
|
+
/**
|
|
1796
|
+
* The Kotlin client's walk: the routes into a partition, then the file.
|
|
1797
|
+
*/
|
|
1798
|
+
const kotlinClientWalk = (api, config) => {
|
|
1799
|
+
const registry = new TypeRegistry(config.camelCaseProperties ?? false, config.unknownEnumCase ?? false);
|
|
1800
|
+
const routes = kotlinRoutesWalk({
|
|
1801
|
+
namespaceName: config.namespaceName,
|
|
1802
|
+
registry
|
|
1803
|
+
});
|
|
1804
|
+
return {
|
|
1805
|
+
processRoute: routes.processRoute,
|
|
1806
|
+
finalize: () => renderKotlinClient(api, routes.finalize(), registry, config)
|
|
1807
|
+
};
|
|
1808
|
+
};
|
|
1809
|
+
/**
|
|
1810
|
+
* Generate a Kotlin API client from a Kizuna api.
|
|
1811
|
+
*
|
|
1812
|
+
* @param api - What `defineConfig` assembled.
|
|
1813
|
+
* @param config - Override the generated names:
|
|
1814
|
+
* - `namespaceName`: the object wrapping shared types.
|
|
1815
|
+
* - `packageName`: optional package declaration for the generated file.
|
|
1816
|
+
*/
|
|
1817
|
+
const generateKotlinClient = (api, config) => walkApi(api, kotlinClientWalk(api, config), { skipHidden: true });
|
|
1794
1818
|
//#endregion
|
|
1795
|
-
export { generateKotlinClient as t };
|
|
1819
|
+
export { kotlinClientWalk as n, generateKotlinClient as t };
|
package/dist/index.cjs
CHANGED
|
@@ -1,17 +1,48 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_generator = require("./generator-
|
|
2
|
+
const require_generator = require("./generator-Cs9NkTCg.cjs");
|
|
3
|
+
let kizunajs_generator = require("kizunajs/generator");
|
|
4
|
+
let zod = require("zod");
|
|
3
5
|
//#region src/client-target.ts
|
|
6
|
+
const KotlinClientOptionsSchema = zod.z.object({
|
|
7
|
+
/**
|
|
8
|
+
* Name of the generated namespace object.
|
|
9
|
+
*
|
|
10
|
+
* @default 'API'
|
|
11
|
+
*/
|
|
12
|
+
namespace: zod.z.string().default("API"),
|
|
13
|
+
/**
|
|
14
|
+
* Package declaration for the generated file, e.g. `com.example.api`.
|
|
15
|
+
*/
|
|
16
|
+
package: zod.z.string().optional(),
|
|
17
|
+
/**
|
|
18
|
+
* Convert wire field names to camelCase properties, mapping the wire name
|
|
19
|
+
* back via `@SerialName`.
|
|
20
|
+
*
|
|
21
|
+
* @default false
|
|
22
|
+
*/
|
|
23
|
+
camelCaseProperties: zod.z.boolean().optional(),
|
|
24
|
+
/**
|
|
25
|
+
* Give every enum an `unknown` case, so a value the client has not been
|
|
26
|
+
* generated for decodes instead of throwing.
|
|
27
|
+
*
|
|
28
|
+
* @default false
|
|
29
|
+
*/
|
|
30
|
+
unknownEnumCase: zod.z.boolean().optional()
|
|
31
|
+
});
|
|
4
32
|
/**
|
|
5
|
-
* A Kotlin client
|
|
33
|
+
* A Kotlin client for `kizuna.config.ts`.
|
|
6
34
|
*
|
|
7
35
|
* @example
|
|
8
|
-
* kotlinClient({
|
|
36
|
+
* kotlinClient({
|
|
37
|
+
* output: './api/APIClient.kt',
|
|
38
|
+
* package: 'com.example.api',
|
|
39
|
+
* });
|
|
9
40
|
*/
|
|
10
|
-
const kotlinClient = (
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
generate: (
|
|
14
|
-
namespaceName: options.namespace
|
|
41
|
+
const kotlinClient = (0, kizunajs_generator.defineClient)({
|
|
42
|
+
target: "kotlin",
|
|
43
|
+
options: KotlinClientOptionsSchema,
|
|
44
|
+
generate: ({ options, api }) => require_generator.kotlinClientWalk(api, {
|
|
45
|
+
namespaceName: options.namespace,
|
|
15
46
|
packageName: options.package,
|
|
16
47
|
camelCaseProperties: options.camelCaseProperties,
|
|
17
48
|
unknownEnumCase: options.unknownEnumCase
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { GeneratedFileOptions } from "kizunajs/generator";
|
|
2
|
+
import * as _$kizunajs from "kizunajs";
|
|
3
|
+
import { ApiDefinition } from "kizunajs";
|
|
4
|
+
import { z } from "zod";
|
|
2
5
|
|
|
3
6
|
//#region src/generator.d.ts
|
|
4
7
|
interface KotlinConfig {
|
|
@@ -38,45 +41,30 @@ interface KotlinConfig {
|
|
|
38
41
|
declare const generateKotlinClient: (api: ApiDefinition, config: KotlinConfig) => string;
|
|
39
42
|
//#endregion
|
|
40
43
|
//#region src/client-target.d.ts
|
|
44
|
+
declare const KotlinClientOptionsSchema: z.ZodObject<{
|
|
45
|
+
namespace: z.ZodDefault<z.ZodString>;
|
|
46
|
+
package: z.ZodOptional<z.ZodString>;
|
|
47
|
+
camelCaseProperties: z.ZodOptional<z.ZodBoolean>;
|
|
48
|
+
unknownEnumCase: z.ZodOptional<z.ZodBoolean>;
|
|
49
|
+
}, z.core.$strip>;
|
|
41
50
|
/**
|
|
42
51
|
* Where a generated Kotlin client is written, and what it is called there.
|
|
43
52
|
*/
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Path the generated file is written to.
|
|
47
|
-
*/
|
|
48
|
-
output: string;
|
|
49
|
-
/**
|
|
50
|
-
* Name of the generated namespace object.
|
|
51
|
-
*
|
|
52
|
-
* @default 'API'
|
|
53
|
-
*/
|
|
54
|
-
namespace?: string;
|
|
55
|
-
/**
|
|
56
|
-
* Package declaration for the generated file, e.g. `com.example.api`.
|
|
57
|
-
*/
|
|
58
|
-
package?: string;
|
|
59
|
-
/**
|
|
60
|
-
* Convert wire field names to camelCase properties, mapping the wire name
|
|
61
|
-
* back via `@SerialName`.
|
|
62
|
-
*
|
|
63
|
-
* @default false
|
|
64
|
-
*/
|
|
65
|
-
camelCaseProperties?: boolean;
|
|
66
|
-
/**
|
|
67
|
-
* Give every enum an `unknown` case, so a value the client has not been
|
|
68
|
-
* generated for decodes instead of throwing.
|
|
69
|
-
*
|
|
70
|
-
* @default false
|
|
71
|
-
*/
|
|
72
|
-
unknownEnumCase?: boolean;
|
|
73
|
-
}
|
|
53
|
+
type KotlinClientOptions = z.input<typeof KotlinClientOptionsSchema> & GeneratedFileOptions;
|
|
74
54
|
/**
|
|
75
|
-
* A Kotlin client
|
|
55
|
+
* A Kotlin client for `kizuna.config.ts`.
|
|
76
56
|
*
|
|
77
57
|
* @example
|
|
78
|
-
* kotlinClient({
|
|
58
|
+
* kotlinClient({
|
|
59
|
+
* output: './api/APIClient.kt',
|
|
60
|
+
* package: 'com.example.api',
|
|
61
|
+
* });
|
|
79
62
|
*/
|
|
80
|
-
declare const kotlinClient: (options:
|
|
63
|
+
declare const kotlinClient: (options: {
|
|
64
|
+
namespace?: string | undefined;
|
|
65
|
+
package?: string | undefined;
|
|
66
|
+
camelCaseProperties?: boolean | undefined;
|
|
67
|
+
unknownEnumCase?: boolean | undefined;
|
|
68
|
+
} & GeneratedFileOptions) => _$kizunajs.GeneratedFile;
|
|
81
69
|
//#endregion
|
|
82
70
|
export { type KotlinClientOptions, type KotlinConfig, generateKotlinClient, kotlinClient };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { GeneratedFileOptions } from "kizunajs/generator";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import * as _$kizunajs from "kizunajs";
|
|
4
|
+
import { ApiDefinition } from "kizunajs";
|
|
2
5
|
|
|
3
6
|
//#region src/generator.d.ts
|
|
4
7
|
interface KotlinConfig {
|
|
@@ -38,45 +41,30 @@ interface KotlinConfig {
|
|
|
38
41
|
declare const generateKotlinClient: (api: ApiDefinition, config: KotlinConfig) => string;
|
|
39
42
|
//#endregion
|
|
40
43
|
//#region src/client-target.d.ts
|
|
44
|
+
declare const KotlinClientOptionsSchema: z.ZodObject<{
|
|
45
|
+
namespace: z.ZodDefault<z.ZodString>;
|
|
46
|
+
package: z.ZodOptional<z.ZodString>;
|
|
47
|
+
camelCaseProperties: z.ZodOptional<z.ZodBoolean>;
|
|
48
|
+
unknownEnumCase: z.ZodOptional<z.ZodBoolean>;
|
|
49
|
+
}, z.core.$strip>;
|
|
41
50
|
/**
|
|
42
51
|
* Where a generated Kotlin client is written, and what it is called there.
|
|
43
52
|
*/
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Path the generated file is written to.
|
|
47
|
-
*/
|
|
48
|
-
output: string;
|
|
49
|
-
/**
|
|
50
|
-
* Name of the generated namespace object.
|
|
51
|
-
*
|
|
52
|
-
* @default 'API'
|
|
53
|
-
*/
|
|
54
|
-
namespace?: string;
|
|
55
|
-
/**
|
|
56
|
-
* Package declaration for the generated file, e.g. `com.example.api`.
|
|
57
|
-
*/
|
|
58
|
-
package?: string;
|
|
59
|
-
/**
|
|
60
|
-
* Convert wire field names to camelCase properties, mapping the wire name
|
|
61
|
-
* back via `@SerialName`.
|
|
62
|
-
*
|
|
63
|
-
* @default false
|
|
64
|
-
*/
|
|
65
|
-
camelCaseProperties?: boolean;
|
|
66
|
-
/**
|
|
67
|
-
* Give every enum an `unknown` case, so a value the client has not been
|
|
68
|
-
* generated for decodes instead of throwing.
|
|
69
|
-
*
|
|
70
|
-
* @default false
|
|
71
|
-
*/
|
|
72
|
-
unknownEnumCase?: boolean;
|
|
73
|
-
}
|
|
53
|
+
type KotlinClientOptions = z.input<typeof KotlinClientOptionsSchema> & GeneratedFileOptions;
|
|
74
54
|
/**
|
|
75
|
-
* A Kotlin client
|
|
55
|
+
* A Kotlin client for `kizuna.config.ts`.
|
|
76
56
|
*
|
|
77
57
|
* @example
|
|
78
|
-
* kotlinClient({
|
|
58
|
+
* kotlinClient({
|
|
59
|
+
* output: './api/APIClient.kt',
|
|
60
|
+
* package: 'com.example.api',
|
|
61
|
+
* });
|
|
79
62
|
*/
|
|
80
|
-
declare const kotlinClient: (options:
|
|
63
|
+
declare const kotlinClient: (options: {
|
|
64
|
+
namespace?: string | undefined;
|
|
65
|
+
package?: string | undefined;
|
|
66
|
+
camelCaseProperties?: boolean | undefined;
|
|
67
|
+
unknownEnumCase?: boolean | undefined;
|
|
68
|
+
} & GeneratedFileOptions) => _$kizunajs.GeneratedFile;
|
|
81
69
|
//#endregion
|
|
82
70
|
export { type KotlinClientOptions, type KotlinConfig, generateKotlinClient, kotlinClient };
|
package/dist/index.mjs
CHANGED
|
@@ -1,16 +1,45 @@
|
|
|
1
|
-
import { t as generateKotlinClient } from "./generator-
|
|
2
|
-
|
|
1
|
+
import { n as kotlinClientWalk, t as generateKotlinClient } from "./generator-uzHpr6im.mjs";
|
|
2
|
+
import { defineClient } from "kizunajs/generator";
|
|
3
|
+
import { z } from "zod";
|
|
3
4
|
/**
|
|
4
|
-
* A Kotlin client
|
|
5
|
+
* A Kotlin client for `kizuna.config.ts`.
|
|
5
6
|
*
|
|
6
7
|
* @example
|
|
7
|
-
* kotlinClient({
|
|
8
|
+
* kotlinClient({
|
|
9
|
+
* output: './api/APIClient.kt',
|
|
10
|
+
* package: 'com.example.api',
|
|
11
|
+
* });
|
|
8
12
|
*/
|
|
9
|
-
const kotlinClient = (
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
const kotlinClient = defineClient({
|
|
14
|
+
target: "kotlin",
|
|
15
|
+
options: z.object({
|
|
16
|
+
/**
|
|
17
|
+
* Name of the generated namespace object.
|
|
18
|
+
*
|
|
19
|
+
* @default 'API'
|
|
20
|
+
*/
|
|
21
|
+
namespace: z.string().default("API"),
|
|
22
|
+
/**
|
|
23
|
+
* Package declaration for the generated file, e.g. `com.example.api`.
|
|
24
|
+
*/
|
|
25
|
+
package: z.string().optional(),
|
|
26
|
+
/**
|
|
27
|
+
* Convert wire field names to camelCase properties, mapping the wire name
|
|
28
|
+
* back via `@SerialName`.
|
|
29
|
+
*
|
|
30
|
+
* @default false
|
|
31
|
+
*/
|
|
32
|
+
camelCaseProperties: z.boolean().optional(),
|
|
33
|
+
/**
|
|
34
|
+
* Give every enum an `unknown` case, so a value the client has not been
|
|
35
|
+
* generated for decodes instead of throwing.
|
|
36
|
+
*
|
|
37
|
+
* @default false
|
|
38
|
+
*/
|
|
39
|
+
unknownEnumCase: z.boolean().optional()
|
|
40
|
+
}),
|
|
41
|
+
generate: ({ options, api }) => kotlinClientWalk(api, {
|
|
42
|
+
namespaceName: options.namespace,
|
|
14
43
|
packageName: options.package,
|
|
15
44
|
camelCaseProperties: options.camelCaseProperties,
|
|
16
45
|
unknownEnumCase: options.unknownEnumCase
|
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.11",
|
|
4
4
|
"description": "Kotlin client generation for Kizuna.js APIs",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"kizunajs",
|
|
@@ -60,18 +60,18 @@
|
|
|
60
60
|
"access": "public"
|
|
61
61
|
},
|
|
62
62
|
"dependencies": {
|
|
63
|
-
"@kizunajs/cli": "2.0.0-beta.
|
|
63
|
+
"@kizunajs/cli": "2.0.0-beta.11"
|
|
64
64
|
},
|
|
65
65
|
"peerDependencies": {
|
|
66
66
|
"zod": "^4.0.0",
|
|
67
|
-
"kizunajs": "2.0.0-beta.
|
|
67
|
+
"kizunajs": "2.0.0-beta.11"
|
|
68
68
|
},
|
|
69
69
|
"devDependencies": {
|
|
70
70
|
"@types/node": "^22.9.0",
|
|
71
71
|
"tsdown": "^0.21.0",
|
|
72
72
|
"typescript": "^6.0.3",
|
|
73
73
|
"zod": "^4.0.0",
|
|
74
|
-
"kizunajs": "2.0.0-beta.
|
|
74
|
+
"kizunajs": "2.0.0-beta.11"
|
|
75
75
|
},
|
|
76
76
|
"scripts": {
|
|
77
77
|
"build": "tsdown src/index.ts src/cli.ts --format esm,cjs --dts --clean --external kizunajs --external @kizunajs/cli --external zod",
|