@kizunajs/kotlin 2.0.0-beta.1 → 2.0.0-beta.10
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-CefUI7SC.cjs} +25 -11
- package/dist/{generator-BvNpSarY.mjs → generator-Ct5OmESk.mjs} +26 -12
- package/dist/index.cjs +1 -1
- package/dist/index.mjs +1 -1
- 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-Ct5OmESk.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
|
}
|
|
@@ -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})`);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createGenerator, 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 } from "kizunajs/generator";
|
|
1
|
+
import { createGenerator, 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 } 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
|
}
|
|
@@ -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})`);
|
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-CefUI7SC.cjs");
|
|
3
3
|
//#region src/client-target.ts
|
|
4
4
|
/**
|
|
5
5
|
* A Kotlin client target for `kizuna.config.ts`.
|
package/dist/index.mjs
CHANGED
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.10",
|
|
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.10"
|
|
64
64
|
},
|
|
65
65
|
"peerDependencies": {
|
|
66
66
|
"zod": "^4.0.0",
|
|
67
|
-
"kizunajs": "2.0.0-beta.
|
|
67
|
+
"kizunajs": "2.0.0-beta.10"
|
|
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.10"
|
|
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",
|