@kizunajs/kotlin 2.0.0-beta.17 → 2.0.0-beta.19

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
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- const require_generator = require("./generator-D2dgwOZe.cjs");
2
+ const require_generator = require("./generator-D4SnsG2Q.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-BYxosxwy.mjs";
2
+ import { t as generateKotlinClient } from "./generator-DT7c2Vo3.mjs";
3
3
  import { mkdirSync, writeFileSync } from "node:fs";
4
4
  import { dirname, resolve } from "node:path";
5
5
  import { parseArgs } from "node:util";
@@ -142,6 +142,7 @@ const objectFields = (schema, registry, hint, shape = (0, kizunajs_generator.rea
142
142
  wireName: key,
143
143
  type: result.expression,
144
144
  optional: result.optional,
145
+ requiredNullable: result.requiredNullable,
145
146
  description: (0, kizunajs_generator.readMetaDescription)(value),
146
147
  isFile: result.isFile,
147
148
  deprecated: deprecation !== void 0,
@@ -264,6 +265,7 @@ const mapUnbrandedType = (schema, registry, hint) => {
264
265
  return {
265
266
  expression: innerResult.expression.endsWith("?") ? innerResult.expression : `${innerResult.expression}?`,
266
267
  optional: true,
268
+ requiredNullable: nullable && !optional,
267
269
  isFile: innerResult.isFile
268
270
  };
269
271
  }
@@ -448,6 +450,7 @@ const sanitizeCaseName = (literal) => {
448
450
  const collectObjectFields = (schema, registry, hint) => {
449
451
  return objectFields(schema, registry, hint);
450
452
  };
453
+ const hasDefault = (field) => field.optional && field.requiredNullable !== true;
451
454
  const objectFieldCount = (schema) => {
452
455
  return Object.keys((0, kizunajs_generator.readObjectShape)(schema) ?? {}).length;
453
456
  };
@@ -678,6 +681,10 @@ const KOTLIN_KEYWORDS = new Set([
678
681
  const escapeKeyword = (name) => {
679
682
  return KOTLIN_KEYWORDS.has(name) ? `\`${name}\`` : name;
680
683
  };
684
+ const defaultArgument = (field) => {
685
+ if (!hasDefault(field)) return "";
686
+ return " = null";
687
+ };
681
688
  const optionalize = (type, optional) => {
682
689
  if (!optional) return type;
683
690
  return type.endsWith("?") ? type : `${type}?`;
@@ -838,7 +845,7 @@ const emitDataClass = (writer, type, ownedTypeMap, ownedTypeLookup, registryName
838
845
  if (!hasFile) writer.line("@Serializable");
839
846
  const params = adjustedFields.map((field) => {
840
847
  const typeExpression = optionalize(field.type, field.optional);
841
- const defaultPart = field.optional ? " = null" : "";
848
+ const defaultPart = defaultArgument(field);
842
849
  const serialNameAnnotation = needsSerialName && (field.name !== field.wireName || KOTLIN_KEYWORDS.has(field.name)) ? `@SerialName(${stringLiteral(field.wireName)}) ` : "";
843
850
  return `${field.deprecated ? `${deprecatedAnnotation(field.deprecationMessage)} ` : ""}${serialNameAnnotation}val ${escapeKeyword(field.name)}: ${typeExpression}${defaultPart}`;
844
851
  });
@@ -931,7 +938,7 @@ const emitSealedClass = (writer, type, registry, ownedTypeMap, ownedTypeLookup)
931
938
  const params = fields.map((field) => {
932
939
  const variantPath = registry.sealedVariantPath(variant.payloadType) ?? variant.payloadType;
933
940
  const typeExpression = optionalize(relativeOwnedPath(field.type, ownedTypeMap, variantPath, registry) ?? ownedTypePath(field.type, ownedTypeMap, registry), field.optional);
934
- const defaultPart = field.optional ? " = null" : "";
941
+ const defaultPart = defaultArgument(field);
935
942
  return `val ${escapeKeyword(field.name)}: ${typeExpression}${defaultPart}`;
936
943
  });
937
944
  if (params.length === 1) writer.line(`data class ${variant.caseName}(${params[0]}) : ${type.name}`);
@@ -1130,7 +1137,7 @@ const groupTypeRef = (operationName, className, context) => `${context.clientNam
1130
1137
  const groupMemberAccessor = (groupLabel, field) => `${groupLabel}.${escapeKeyword(field.name)}`;
1131
1138
  const groupClassField = (field) => {
1132
1139
  const typeExpression = optionalize(field.type, field.optional);
1133
- const defaultPart = field.optional ? " = null" : "";
1140
+ const defaultPart = defaultArgument(field);
1134
1141
  return `val ${escapeKeyword(field.name)}: ${typeExpression}${defaultPart}`;
1135
1142
  };
1136
1143
  const emitGroupClass = (writer, className, fields, method, context) => {
@@ -1189,13 +1196,13 @@ const methodChannels = (method) => {
1189
1196
  if (method.query.length > 0) channels.push({
1190
1197
  varName: "query",
1191
1198
  className: "Query",
1192
- required: method.query.some((field) => !field.optional),
1199
+ required: method.query.some((field) => !hasDefault(field)),
1193
1200
  fields: method.query
1194
1201
  });
1195
1202
  if (method.headers.length > 0) channels.push({
1196
1203
  varName: "headers",
1197
1204
  className: "Headers",
1198
- required: method.headers.some((field) => !field.optional),
1205
+ required: method.headers.some((field) => !hasDefault(field)),
1199
1206
  fields: method.headers
1200
1207
  });
1201
1208
  if (hasBodyGroup(method)) {
@@ -1244,7 +1251,7 @@ const emitArgsScaffolding = (writer, method, context) => {
1244
1251
  };
1245
1252
  const factorySignature = (channel) => channel.fields.map((field) => {
1246
1253
  const typeExpression = optionalize(field.type, field.optional);
1247
- const defaultPart = field.optional ? " = null" : "";
1254
+ const defaultPart = defaultArgument(field);
1248
1255
  return `${escapeKeyword(field.name)}: ${typeExpression}${defaultPart}`;
1249
1256
  }).join(", ");
1250
1257
  const factoryConstruction = (channel) => {
@@ -1301,10 +1308,16 @@ const emitBodyEncoding = (writer, method, context) => {
1301
1308
  if (body.kind === "multipart") {
1302
1309
  writer.line("val multipartBuilder = MultipartBody.Builder().setType(MultipartBody.FORM)");
1303
1310
  for (const field of body.multipartFields) {
1304
- const accessor = groupMemberAccessor("body", field);
1305
- const isFlattenedFile = body.flattened.find((flatField) => flatField.name === field.name)?.isFile === true;
1306
- if (field.isFile || isFlattenedFile) writer.line(`multipartBuilder.addFormDataPart(${stringLiteral(field.wireName)}, ${accessor}.filename, ${accessor}.data.toRequestBody(${accessor}.mimeType.toMediaType()))`);
1307
- else writer.line(`multipartBuilder.addFormDataPart(${stringLiteral(field.wireName)}, ${accessor}.toString())`);
1311
+ const flattenedField = body.flattened.find((flatField) => flatField.name === field.name);
1312
+ const isFile = field.isFile || flattenedField?.isFile === true;
1313
+ const append = (accessor) => {
1314
+ if (isFile) writer.line(`multipartBuilder.addFormDataPart(${stringLiteral(field.wireName)}, ${accessor}.filename, ${accessor}.data.toRequestBody(${accessor}.mimeType.toMediaType()))`);
1315
+ else writer.line(`multipartBuilder.addFormDataPart(${stringLiteral(field.wireName)}, ${accessor}.toString())`);
1316
+ };
1317
+ if (flattenedField?.optional === true) writer.block(`if (${groupMemberAccessor("body", field)} != null)`, () => {
1318
+ append(groupMemberAccessor("body", field));
1319
+ });
1320
+ else append(groupMemberAccessor("body", field));
1308
1321
  }
1309
1322
  writer.line("requestBody = multipartBuilder.build()");
1310
1323
  return;
@@ -1664,14 +1677,14 @@ const emitClient = (writer, config, partition, context, typesByOperation, regist
1664
1677
  const allMethods = [...flatMethods, ...groups.flatMap((group) => group.methods)];
1665
1678
  const usesMultipart = allMethods.some((method) => method.body?.kind === "multipart");
1666
1679
  const contextFields = context.requestContextFields;
1667
- const contextRequired = contextFields.some((field) => !field.optional);
1680
+ const contextRequired = contextFields.some((field) => !hasDefault(field));
1668
1681
  const contextParam = contextFields.length > 0 ? `requestContext: RequestContext${contextRequired ? "" : " = RequestContext()"}, ` : "";
1669
1682
  writer.blank();
1670
1683
  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)`, () => {
1671
1684
  if (contextFields.length > 0) {
1672
1685
  writer.blank();
1673
1686
  writer.line("/** Values sent as headers on every request, from the api's request context. */");
1674
- emitConstructorClass(writer, "data class RequestContext", contextFields.map((field) => `val ${escapeKeyword(field.name)}: ${field.type}${field.optional ? " = null" : ""}`));
1687
+ emitConstructorClass(writer, "data class RequestContext", contextFields.map((field) => `val ${escapeKeyword(field.name)}: ${field.type}${defaultArgument(field)}`));
1675
1688
  writer.blank();
1676
1689
  writer.block("private val requestContextHeaders: Map<String, String> = buildMap", () => {
1677
1690
  for (const field of contextFields) if (field.optional) writer.line(`if (requestContext.${escapeKeyword(field.name)} != null) put(${stringLiteral(field.wireName)}, requestContext.${escapeKeyword(field.name)}!!)`);
@@ -142,6 +142,7 @@ const objectFields = (schema, registry, hint, shape = readObjectShape(schema) ??
142
142
  wireName: key,
143
143
  type: result.expression,
144
144
  optional: result.optional,
145
+ requiredNullable: result.requiredNullable,
145
146
  description: readMetaDescription(value),
146
147
  isFile: result.isFile,
147
148
  deprecated: deprecation !== void 0,
@@ -264,6 +265,7 @@ const mapUnbrandedType = (schema, registry, hint) => {
264
265
  return {
265
266
  expression: innerResult.expression.endsWith("?") ? innerResult.expression : `${innerResult.expression}?`,
266
267
  optional: true,
268
+ requiredNullable: nullable && !optional,
267
269
  isFile: innerResult.isFile
268
270
  };
269
271
  }
@@ -448,6 +450,7 @@ const sanitizeCaseName = (literal) => {
448
450
  const collectObjectFields = (schema, registry, hint) => {
449
451
  return objectFields(schema, registry, hint);
450
452
  };
453
+ const hasDefault = (field) => field.optional && field.requiredNullable !== true;
451
454
  const objectFieldCount = (schema) => {
452
455
  return Object.keys(readObjectShape(schema) ?? {}).length;
453
456
  };
@@ -678,6 +681,10 @@ const KOTLIN_KEYWORDS = new Set([
678
681
  const escapeKeyword = (name) => {
679
682
  return KOTLIN_KEYWORDS.has(name) ? `\`${name}\`` : name;
680
683
  };
684
+ const defaultArgument = (field) => {
685
+ if (!hasDefault(field)) return "";
686
+ return " = null";
687
+ };
681
688
  const optionalize = (type, optional) => {
682
689
  if (!optional) return type;
683
690
  return type.endsWith("?") ? type : `${type}?`;
@@ -838,7 +845,7 @@ const emitDataClass = (writer, type, ownedTypeMap, ownedTypeLookup, registryName
838
845
  if (!hasFile) writer.line("@Serializable");
839
846
  const params = adjustedFields.map((field) => {
840
847
  const typeExpression = optionalize(field.type, field.optional);
841
- const defaultPart = field.optional ? " = null" : "";
848
+ const defaultPart = defaultArgument(field);
842
849
  const serialNameAnnotation = needsSerialName && (field.name !== field.wireName || KOTLIN_KEYWORDS.has(field.name)) ? `@SerialName(${stringLiteral(field.wireName)}) ` : "";
843
850
  return `${field.deprecated ? `${deprecatedAnnotation(field.deprecationMessage)} ` : ""}${serialNameAnnotation}val ${escapeKeyword(field.name)}: ${typeExpression}${defaultPart}`;
844
851
  });
@@ -931,7 +938,7 @@ const emitSealedClass = (writer, type, registry, ownedTypeMap, ownedTypeLookup)
931
938
  const params = fields.map((field) => {
932
939
  const variantPath = registry.sealedVariantPath(variant.payloadType) ?? variant.payloadType;
933
940
  const typeExpression = optionalize(relativeOwnedPath(field.type, ownedTypeMap, variantPath, registry) ?? ownedTypePath(field.type, ownedTypeMap, registry), field.optional);
934
- const defaultPart = field.optional ? " = null" : "";
941
+ const defaultPart = defaultArgument(field);
935
942
  return `val ${escapeKeyword(field.name)}: ${typeExpression}${defaultPart}`;
936
943
  });
937
944
  if (params.length === 1) writer.line(`data class ${variant.caseName}(${params[0]}) : ${type.name}`);
@@ -1130,7 +1137,7 @@ const groupTypeRef = (operationName, className, context) => `${context.clientNam
1130
1137
  const groupMemberAccessor = (groupLabel, field) => `${groupLabel}.${escapeKeyword(field.name)}`;
1131
1138
  const groupClassField = (field) => {
1132
1139
  const typeExpression = optionalize(field.type, field.optional);
1133
- const defaultPart = field.optional ? " = null" : "";
1140
+ const defaultPart = defaultArgument(field);
1134
1141
  return `val ${escapeKeyword(field.name)}: ${typeExpression}${defaultPart}`;
1135
1142
  };
1136
1143
  const emitGroupClass = (writer, className, fields, method, context) => {
@@ -1189,13 +1196,13 @@ const methodChannels = (method) => {
1189
1196
  if (method.query.length > 0) channels.push({
1190
1197
  varName: "query",
1191
1198
  className: "Query",
1192
- required: method.query.some((field) => !field.optional),
1199
+ required: method.query.some((field) => !hasDefault(field)),
1193
1200
  fields: method.query
1194
1201
  });
1195
1202
  if (method.headers.length > 0) channels.push({
1196
1203
  varName: "headers",
1197
1204
  className: "Headers",
1198
- required: method.headers.some((field) => !field.optional),
1205
+ required: method.headers.some((field) => !hasDefault(field)),
1199
1206
  fields: method.headers
1200
1207
  });
1201
1208
  if (hasBodyGroup(method)) {
@@ -1244,7 +1251,7 @@ const emitArgsScaffolding = (writer, method, context) => {
1244
1251
  };
1245
1252
  const factorySignature = (channel) => channel.fields.map((field) => {
1246
1253
  const typeExpression = optionalize(field.type, field.optional);
1247
- const defaultPart = field.optional ? " = null" : "";
1254
+ const defaultPart = defaultArgument(field);
1248
1255
  return `${escapeKeyword(field.name)}: ${typeExpression}${defaultPart}`;
1249
1256
  }).join(", ");
1250
1257
  const factoryConstruction = (channel) => {
@@ -1301,10 +1308,16 @@ const emitBodyEncoding = (writer, method, context) => {
1301
1308
  if (body.kind === "multipart") {
1302
1309
  writer.line("val multipartBuilder = MultipartBody.Builder().setType(MultipartBody.FORM)");
1303
1310
  for (const field of body.multipartFields) {
1304
- const accessor = groupMemberAccessor("body", field);
1305
- const isFlattenedFile = body.flattened.find((flatField) => flatField.name === field.name)?.isFile === true;
1306
- if (field.isFile || isFlattenedFile) writer.line(`multipartBuilder.addFormDataPart(${stringLiteral(field.wireName)}, ${accessor}.filename, ${accessor}.data.toRequestBody(${accessor}.mimeType.toMediaType()))`);
1307
- else writer.line(`multipartBuilder.addFormDataPart(${stringLiteral(field.wireName)}, ${accessor}.toString())`);
1311
+ const flattenedField = body.flattened.find((flatField) => flatField.name === field.name);
1312
+ const isFile = field.isFile || flattenedField?.isFile === true;
1313
+ const append = (accessor) => {
1314
+ if (isFile) writer.line(`multipartBuilder.addFormDataPart(${stringLiteral(field.wireName)}, ${accessor}.filename, ${accessor}.data.toRequestBody(${accessor}.mimeType.toMediaType()))`);
1315
+ else writer.line(`multipartBuilder.addFormDataPart(${stringLiteral(field.wireName)}, ${accessor}.toString())`);
1316
+ };
1317
+ if (flattenedField?.optional === true) writer.block(`if (${groupMemberAccessor("body", field)} != null)`, () => {
1318
+ append(groupMemberAccessor("body", field));
1319
+ });
1320
+ else append(groupMemberAccessor("body", field));
1308
1321
  }
1309
1322
  writer.line("requestBody = multipartBuilder.build()");
1310
1323
  return;
@@ -1664,14 +1677,14 @@ const emitClient = (writer, config, partition, context, typesByOperation, regist
1664
1677
  const allMethods = [...flatMethods, ...groups.flatMap((group) => group.methods)];
1665
1678
  const usesMultipart = allMethods.some((method) => method.body?.kind === "multipart");
1666
1679
  const contextFields = context.requestContextFields;
1667
- const contextRequired = contextFields.some((field) => !field.optional);
1680
+ const contextRequired = contextFields.some((field) => !hasDefault(field));
1668
1681
  const contextParam = contextFields.length > 0 ? `requestContext: RequestContext${contextRequired ? "" : " = RequestContext()"}, ` : "";
1669
1682
  writer.blank();
1670
1683
  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)`, () => {
1671
1684
  if (contextFields.length > 0) {
1672
1685
  writer.blank();
1673
1686
  writer.line("/** Values sent as headers on every request, from the api's request context. */");
1674
- emitConstructorClass(writer, "data class RequestContext", contextFields.map((field) => `val ${escapeKeyword(field.name)}: ${field.type}${field.optional ? " = null" : ""}`));
1687
+ emitConstructorClass(writer, "data class RequestContext", contextFields.map((field) => `val ${escapeKeyword(field.name)}: ${field.type}${defaultArgument(field)}`));
1675
1688
  writer.blank();
1676
1689
  writer.block("private val requestContextHeaders: Map<String, String> = buildMap", () => {
1677
1690
  for (const field of contextFields) if (field.optional) writer.line(`if (requestContext.${escapeKeyword(field.name)} != null) put(${stringLiteral(field.wireName)}, requestContext.${escapeKeyword(field.name)}!!)`);
package/dist/index.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_generator = require("./generator-D2dgwOZe.cjs");
2
+ const require_generator = require("./generator-D4SnsG2Q.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-BYxosxwy.mjs";
1
+ import { n as kotlinClientWalk, t as generateKotlinClient } from "./generator-DT7c2Vo3.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.17",
3
+ "version": "2.0.0-beta.19",
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.17"
63
+ "@kizunajs/cli": "2.0.0-beta.19"
64
64
  },
65
65
  "peerDependencies": {
66
- "kizunajs": "2.0.0-beta.17",
66
+ "kizunajs": "2.0.0-beta.19",
67
67
  "zod": "^4.0.0"
68
68
  },
69
69
  "devDependencies": {
70
- "kizunajs": "2.0.0-beta.17",
70
+ "kizunajs": "2.0.0-beta.19",
71
71
  "@types/node": "^22.9.0",
72
72
  "tsdown": "^0.21.0",
73
73
  "typescript": "^6.0.3",