@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 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-kotlin generate --config kizuna.config.ts --out android/app/src/main/kotlin/com/example/APIClient.kt --namespace-name API --package com.example
31
+ kizuna generate
15
32
  ```
16
33
 
17
34
  ## Documentation
package/dist/cli.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- const require_generator = require("./generator-s2pn7vqP.cjs");
2
+ const require_generator = require("./generator-CefUI7SC.cjs");
3
3
  let node_fs = require("node:fs");
4
4
  let node_path = require("node:path");
5
5
  let node_util = require("node:util");
package/dist/cli.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { t as generateKotlinClient } from "./generator-BvNpSarY.mjs";
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<${mapType(element, registry, `${hint}Item`).expression.replace(/\?$/, "")}>`,
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 -> calls[event.data.id] = (calls[event.data.id] ?: ToolCallRecord(event.data.id, event.data.name)).copy(call = event.data)");
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 = json.decodeFromString<${resolved}>(data.decodeToString())`);
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(`json.decodeFromString<${resolved}>(data.decodeToString())`);
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(`json.decodeFromString<${resolved}>(data.decodeToString())`);
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<${mapType(element, registry, `${hint}Item`).expression.replace(/\?$/, "")}>`,
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 -> calls[event.data.id] = (calls[event.data.id] ?: ToolCallRecord(event.data.id, event.data.name)).copy(call = event.data)");
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 = json.decodeFromString<${resolved}>(data.decodeToString())`);
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(`json.decodeFromString<${resolved}>(data.decodeToString())`);
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(`json.decodeFromString<${resolved}>(data.decodeToString())`);
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-s2pn7vqP.cjs");
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
@@ -1,4 +1,4 @@
1
- import { t as generateKotlinClient } from "./generator-BvNpSarY.mjs";
1
+ import { t as generateKotlinClient } from "./generator-Ct5OmESk.mjs";
2
2
  //#region src/client-target.ts
3
3
  /**
4
4
  * A Kotlin client target for `kizuna.config.ts`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kizunajs/kotlin",
3
- "version": "2.0.0-beta.1",
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.1"
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.1"
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.1"
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",