@arrirpc/codegen-kotlin 0.45.0

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/index.mjs ADDED
@@ -0,0 +1,1924 @@
1
+ import fs from 'node:fs';
2
+ import { removeDisallowedChars, camelCase, stringStartsWithNumber, pascalCase, isServiceDefinition, isRpcDefinition, defineClientGeneratorPlugin, unflattenProcedures, isSchemaFormType, isSchemaFormEnum, isSchemaFormProperties, isSchemaFormElements, isSchemaFormValues, isSchemaFormDiscriminator, isSchemaFormRef } from '@arrirpc/codegen-utils';
3
+
4
+ const reservedIdentifierKeywords = [
5
+ "as",
6
+ "as?",
7
+ "break",
8
+ "class",
9
+ "continue",
10
+ "do",
11
+ "else",
12
+ "false",
13
+ "for",
14
+ "fun",
15
+ "if",
16
+ "in",
17
+ "!in",
18
+ "interface",
19
+ "is",
20
+ "!is",
21
+ "null",
22
+ "object",
23
+ "package",
24
+ "return",
25
+ "super",
26
+ "this",
27
+ "throw",
28
+ "true",
29
+ "try",
30
+ "typealias",
31
+ "typeof",
32
+ "val",
33
+ "var",
34
+ "when",
35
+ "while"
36
+ ];
37
+ const illegalCharacters = "+-*/%=&|!<>[]?:.@$;";
38
+ function kotlinIdentifier(input) {
39
+ const name = removeDisallowedChars(camelCase(input), illegalCharacters);
40
+ if (stringStartsWithNumber(name) || reservedIdentifierKeywords.includes(name)) {
41
+ return `\`${name}\``;
42
+ }
43
+ return name;
44
+ }
45
+ function kotlinClassName(input) {
46
+ const name = removeDisallowedChars(
47
+ pascalCase(input, { normalize: true }),
48
+ illegalCharacters
49
+ );
50
+ if (stringStartsWithNumber(name) || reservedIdentifierKeywords.includes(name)) {
51
+ return `_${name}`;
52
+ }
53
+ return name;
54
+ }
55
+ function getClassName(schema, context) {
56
+ if (schema.metadata?.id) {
57
+ const className2 = kotlinClassName(
58
+ pascalCase(schema.metadata.id, {
59
+ normalize: true
60
+ })
61
+ );
62
+ return `${context.modelPrefix}${className2}`;
63
+ }
64
+ const depth = instanceDepth(context);
65
+ if (depth === 1 && !context.discriminatorKey) {
66
+ const className2 = kotlinClassName(
67
+ pascalCase(context.instancePath.replace("/", ""), {
68
+ normalize: true
69
+ })
70
+ );
71
+ return `${context.modelPrefix}${className2}`;
72
+ }
73
+ if (context.discriminatorParentId && context.discriminatorKey && context.discriminatorValue) {
74
+ const className2 = kotlinClassName(
75
+ pascalCase(
76
+ `${context.discriminatorParentId}_${context.discriminatorValue}`,
77
+ { normalize: true }
78
+ )
79
+ );
80
+ return `${context.modelPrefix}${className2}`;
81
+ }
82
+ const className = kotlinClassName(
83
+ pascalCase(
84
+ context.instancePath.split("/").join("_").split("[").join("_").split("]").join("_"),
85
+ {
86
+ normalize: true
87
+ }
88
+ )
89
+ );
90
+ return `${context.modelPrefix}${className}`;
91
+ }
92
+ function instanceDepth(context) {
93
+ const parts = context.instancePath.split("/");
94
+ return parts.length - 1;
95
+ }
96
+ function isNullable(schema, context) {
97
+ return schema.nullable === true || context.isOptional === true;
98
+ }
99
+
100
+ function kotlinAnyFromSchema(schema, context) {
101
+ const nullable = isNullable(schema, context);
102
+ const defaultValue = nullable ? "null" : "JsonNull";
103
+ return {
104
+ typeName: "JsonElement",
105
+ isNullable: nullable,
106
+ defaultValue,
107
+ fromJson(input) {
108
+ if (context.isOptional) {
109
+ return `when (${input}) {
110
+ null -> null
111
+ else -> ${input}
112
+ }`;
113
+ }
114
+ if (schema.nullable) {
115
+ return `when (${input}) {
116
+ JsonNull -> null
117
+ null -> null
118
+ else -> ${input}
119
+ }`;
120
+ }
121
+ return `when (${input}) {
122
+ is JsonElement -> ${input}!!
123
+ else -> JsonNull
124
+ }`;
125
+ },
126
+ toJson(input, target) {
127
+ if (schema.nullable) {
128
+ return `${target} += when (${input}) {
129
+ null -> "null"
130
+ else -> JsonInstance.encodeToString(${input})
131
+ }`;
132
+ }
133
+ return `${target} += JsonInstance.encodeToString(${input})`;
134
+ },
135
+ toQueryString() {
136
+ return `__logError("[WARNING] any's cannot be serialized to query params. Skipping field at ${context.instancePath}.")`;
137
+ },
138
+ content: ""
139
+ };
140
+ }
141
+
142
+ function kotlinArrayFromSchema(schema, context) {
143
+ const nullable = isNullable(schema, context);
144
+ const defaultValue = nullable ? "null" : "mutableListOf()";
145
+ const subType = kotlinTypeFromSchema(schema.elements, {
146
+ modelPrefix: context.modelPrefix,
147
+ clientName: context.clientName,
148
+ clientVersion: context.clientVersion,
149
+ instancePath: `${context.instancePath}/[element]`,
150
+ schemaPath: `${context.schemaPath}/elements`,
151
+ existingTypeIds: context.existingTypeIds
152
+ });
153
+ const typeName = `MutableList<${subType.typeName}${subType.isNullable ? "?" : ""}>`;
154
+ return {
155
+ typeName,
156
+ isNullable: nullable,
157
+ defaultValue,
158
+ fromJson(input) {
159
+ if (nullable) {
160
+ return `when (${input}) {
161
+ is JsonArray -> {
162
+ val __value: ${typeName} = mutableListOf()
163
+ for (__element in ${input}!!.jsonArray) {
164
+ __value.add(
165
+ ${subType.fromJson("__element")}
166
+ )
167
+ }
168
+ __value
169
+ }
170
+
171
+ else -> null
172
+ }`;
173
+ }
174
+ return `when (${input}) {
175
+ is JsonArray -> {
176
+ val __value: ${typeName} = mutableListOf()
177
+ for (__element in ${input}!!.jsonArray) {
178
+ __value.add(
179
+ ${subType.fromJson("__element")}
180
+ )
181
+ }
182
+ __value
183
+ }
184
+
185
+ else -> mutableListOf()
186
+ }`;
187
+ },
188
+ toJson(input, target) {
189
+ if (schema.nullable) {
190
+ return `if (${input} == null) {
191
+ ${target} += "null"
192
+ } else {
193
+ ${target} += "["
194
+ for ((__index, __element) in ${input}.withIndex()) {
195
+ if (__index != 0) {
196
+ ${target} += ","
197
+ }
198
+ ${subType.toJson("__element", target)}
199
+ }
200
+ ${target} += "]"
201
+ }`;
202
+ }
203
+ return `${target} += "["
204
+ for ((__index, __element) in ${input}.withIndex()) {
205
+ if (__index != 0) {
206
+ ${target} += ","
207
+ }
208
+ ${subType.toJson("__element", target)}
209
+ }
210
+ ${target} += "]"`;
211
+ },
212
+ toQueryString() {
213
+ return `__logError("[WARNING] arrays cannot be serialized to query params. Skipping field at ${context.instancePath}.")`;
214
+ },
215
+ content: subType.content
216
+ };
217
+ }
218
+
219
+ function kotlinObjectFromSchema(schema, context) {
220
+ const className = getClassName(schema, context);
221
+ const nullable = isNullable(schema, context);
222
+ const defaultValue = nullable ? "null" : `${className}.new()`;
223
+ const result = {
224
+ typeName: className,
225
+ isNullable: nullable,
226
+ defaultValue,
227
+ fromJson(input, key) {
228
+ if (nullable) {
229
+ return `when (${input}) {
230
+ is JsonObject -> ${className}.fromJsonElement(
231
+ ${input}!!,
232
+ "$instancePath/${key}",
233
+ )
234
+
235
+ else -> null
236
+ }`;
237
+ }
238
+ return `when (${input}) {
239
+ is JsonObject -> ${className}.fromJsonElement(
240
+ ${input}!!,
241
+ "$instancePath/${key}",
242
+ )
243
+
244
+ else -> ${className}.new()
245
+ }`;
246
+ },
247
+ toJson(input, target) {
248
+ if (schema.nullable) {
249
+ return `${target} += ${input}?.toJson()`;
250
+ }
251
+ return `${target} += ${input}.toJson()`;
252
+ },
253
+ toQueryString() {
254
+ return `__logError("[WARNING] nested objects cannot be serialized to query params. Skipping field at ${context.instancePath}.")`;
255
+ },
256
+ content: ""
257
+ };
258
+ if (context.existingTypeIds.includes(className)) {
259
+ return result;
260
+ }
261
+ const subContent = [];
262
+ const kotlinKeys = [];
263
+ const fieldParts = [];
264
+ const defaultParts = [];
265
+ const toJsonParts = [`var output = "{"`];
266
+ const toQueryParts = ["val queryParts = mutableListOf<String>()"];
267
+ const fromJsonParts = [];
268
+ const requiredKeys = Object.keys(schema.properties);
269
+ const optionalKeys = Object.keys(schema.optionalProperties ?? {});
270
+ const hasKnownKeys = requiredKeys.length > 0 || context.discriminatorKey && context.discriminatorValue;
271
+ if (!hasKnownKeys) {
272
+ toJsonParts.push("var hasProperties = false");
273
+ }
274
+ if (context.discriminatorKey && context.discriminatorValue) {
275
+ toJsonParts.push(
276
+ `output += "\\"${context.discriminatorKey}\\":\\"${context.discriminatorValue}\\""`
277
+ );
278
+ toQueryParts.push(
279
+ `queryParts.add("${context.discriminatorKey}=${context.discriminatorValue}")`
280
+ );
281
+ }
282
+ for (let i = 0; i < requiredKeys.length; i++) {
283
+ const key = requiredKeys[i];
284
+ const kotlinKey = kotlinIdentifier(key);
285
+ kotlinKeys.push(kotlinKey);
286
+ const prop = schema.properties[key];
287
+ const type = kotlinTypeFromSchema(prop, {
288
+ modelPrefix: context.modelPrefix,
289
+ clientName: context.clientName,
290
+ clientVersion: context.clientVersion,
291
+ instancePath: `/${className}/${key}`,
292
+ schemaPath: `${context.schemaPath}/properties/${key}`,
293
+ existingTypeIds: context.existingTypeIds
294
+ });
295
+ if (type.content) {
296
+ subContent.push(type.content);
297
+ }
298
+ fieldParts.push(
299
+ ` val ${kotlinKey}: ${type.typeName}${type.isNullable ? "?" : ""},`
300
+ );
301
+ if (i === 0 && !context.discriminatorKey) {
302
+ toJsonParts.push(`output += "\\"${key}\\":"`);
303
+ } else {
304
+ toJsonParts.push(`output += ",\\"${key}\\":"`);
305
+ }
306
+ toJsonParts.push(type.toJson(kotlinKey, "output"));
307
+ toQueryParts.push(type.toQueryString(kotlinKey, "queryParts", key));
308
+ fromJsonParts.push(
309
+ `val ${kotlinKey}: ${type.typeName}${type.isNullable ? "?" : ""} = ${type.fromJson(`__input.jsonObject["${key}"]`, key)}`
310
+ );
311
+ defaultParts.push(
312
+ ` ${kotlinKey} = ${type.defaultValue},`
313
+ );
314
+ }
315
+ for (let i = 0; i < optionalKeys.length; i++) {
316
+ const isFirst = i === 0 && requiredKeys.length === 0 && !context.discriminatorKey;
317
+ const isLast = i === optionalKeys.length - 1;
318
+ const key = optionalKeys[i];
319
+ const kotlinKey = kotlinIdentifier(key);
320
+ kotlinKeys.push(kotlinKey);
321
+ const type = kotlinTypeFromSchema(schema.optionalProperties[key], {
322
+ modelPrefix: context.modelPrefix,
323
+ clientName: context.clientName,
324
+ clientVersion: context.clientVersion,
325
+ instancePath: `/${className}/${key}`,
326
+ schemaPath: `${context.schemaPath}/optionalProperties/${key}`,
327
+ existingTypeIds: context.existingTypeIds,
328
+ isOptional: true
329
+ });
330
+ if (type.content) {
331
+ subContent.push(type.content);
332
+ }
333
+ const addCommaPart = isFirst ? "" : `
334
+ if (hasProperties) output += ","
335
+ `;
336
+ fieldParts.push(` val ${kotlinKey}: ${type.typeName}? = null,`);
337
+ if (hasKnownKeys) {
338
+ toJsonParts.push(`if (${kotlinKey} != null) {
339
+ output += ",\\"${key}\\":"
340
+ ${type.toJson(kotlinKey, "output")}
341
+ }`);
342
+ } else {
343
+ toJsonParts.push(`if (${kotlinKey} != null) {${addCommaPart}
344
+ output += "\\"${key}\\":"
345
+ ${type.toJson(kotlinKey, "output")}
346
+ ${isLast ? "" : " hasProperties = true"}
347
+ }`);
348
+ }
349
+ toQueryParts.push(type.toQueryString(kotlinKey, "queryParts", key));
350
+ fromJsonParts.push(
351
+ `val ${kotlinKey}: ${type.typeName}? = ${type.fromJson(`__input.jsonObject["${key}"]`, key)}`
352
+ );
353
+ }
354
+ toJsonParts.push('output += "}"');
355
+ toJsonParts.push("return output");
356
+ toQueryParts.push('return queryParts.joinToString("&")');
357
+ const implementedClass = context.discriminatorParentId ?? `${context.clientName}Model`;
358
+ let discriminatorField = "";
359
+ if (context.discriminatorKey && context.discriminatorValue) {
360
+ discriminatorField = `
361
+ override val ${kotlinIdentifier(context.discriminatorKey)} get() = "${context.discriminatorValue}"
362
+ `;
363
+ }
364
+ const content = `data class ${className}(
365
+ ${fieldParts.join("\n")}
366
+ ) : ${implementedClass} {${discriminatorField}
367
+ override fun toJson(): String {
368
+ ${toJsonParts.join("\n")}
369
+ }
370
+
371
+ override fun toUrlQueryParams(): String {
372
+ ${toQueryParts.join("\n")}
373
+ }
374
+
375
+ companion object Factory : ${context.clientName}ModelFactory<${className}> {
376
+ @JvmStatic
377
+ override fun new(): ${className} {
378
+ return ${className}(
379
+ ${defaultParts.join("\n")}
380
+ )
381
+ }
382
+
383
+ @JvmStatic
384
+ override fun fromJson(input: String): ${className} {
385
+ return fromJsonElement(JsonInstance.parseToJsonElement(input))
386
+ }
387
+
388
+ @JvmStatic
389
+ override fun fromJsonElement(__input: JsonElement, instancePath: String): ${className} {
390
+ if (__input !is JsonObject) {
391
+ __logError("[WARNING] ${className}.fromJsonElement() expected kotlinx.serialization.json.JsonObject at $instancePath. Got \${__input.javaClass}. Initializing empty ${className}.")
392
+ return new()
393
+ }
394
+ ${fromJsonParts.join("\n")}
395
+ return ${className}(
396
+ ${kotlinKeys.join(",\n ")},
397
+ )
398
+ }
399
+ }
400
+ }
401
+
402
+ ${subContent.join("\n\n")}`;
403
+ context.existingTypeIds.push(className);
404
+ return {
405
+ ...result,
406
+ content
407
+ };
408
+ }
409
+
410
+ function kotlinDiscriminatorFromSchema(schema, context) {
411
+ const kotlinDiscriminatorKey = kotlinIdentifier(schema.discriminator);
412
+ const className = getClassName(schema, context);
413
+ const nullable = isNullable(schema, context);
414
+ const subTypes = [];
415
+ const subContent = [];
416
+ for (const key of Object.keys(schema.mapping)) {
417
+ const subSchema = schema.mapping[key];
418
+ const subType = kotlinObjectFromSchema(subSchema, {
419
+ modelPrefix: context.modelPrefix,
420
+ clientName: context.clientName,
421
+ clientVersion: context.clientVersion,
422
+ instancePath: context.instancePath,
423
+ schemaPath: `${context.schemaPath}/mapping/${key}`,
424
+ existingTypeIds: context.existingTypeIds,
425
+ discriminatorKey: schema.discriminator,
426
+ discriminatorValue: key,
427
+ discriminatorParentId: className
428
+ });
429
+ subTypes.push({
430
+ typeName: subType.typeName,
431
+ discriminatorValue: key
432
+ });
433
+ if (subType.content) {
434
+ subContent.push(subType.content);
435
+ }
436
+ }
437
+ if (subTypes.length === 0) {
438
+ throw new Error("Discriminator schemas must have at least one mapping");
439
+ }
440
+ const defaultValue = nullable ? "null" : `${className}.new()`;
441
+ const result = {
442
+ typeName: className,
443
+ isNullable: nullable,
444
+ defaultValue,
445
+ fromJson(input, key) {
446
+ return `when (${input}) {
447
+ is JsonObject -> ${className}.fromJsonElement(
448
+ ${input}!!,
449
+ "$instancePath/${key}",
450
+ )
451
+ else -> ${defaultValue}
452
+ }`;
453
+ },
454
+ toJson(input, target) {
455
+ if (schema.nullable) {
456
+ return `${target} += ${input}?.toJson()`;
457
+ }
458
+ return `${target} += ${input}.toJson()`;
459
+ },
460
+ toQueryString() {
461
+ return `__logError("[WARNING] nested objects cannot be serialized to query params. Skipping field at ${context.instancePath}.")`;
462
+ },
463
+ content: ""
464
+ };
465
+ if (context.existingTypeIds.includes(className)) {
466
+ return result;
467
+ }
468
+ const content = `sealed interface ${className} : ${context.clientName}Model {
469
+ val ${kotlinDiscriminatorKey}: String
470
+
471
+ companion object Factory : ${context.clientName}ModelFactory<${className}> {
472
+ @JvmStatic
473
+ override fun new(): ${className} {
474
+ return ${subTypes[0].typeName}.new()
475
+ }
476
+
477
+ @JvmStatic
478
+ override fun fromJson(input: String): ${className} {
479
+ return fromJsonElement(JsonInstance.parseToJsonElement(input))
480
+ }
481
+
482
+ @JvmStatic
483
+ override fun fromJsonElement(__input: JsonElement, instancePath: String): ${className} {
484
+ if (__input !is JsonObject) {
485
+ __logError("[WARNING] Discriminator.fromJsonElement() expected kotlinx.serialization.json.JsonObject at $instancePath. Got \${__input.javaClass}. Initializing empty ${className}.")
486
+ return new()
487
+ }
488
+ return when (__input.jsonObject["${schema.discriminator}"]) {
489
+ is JsonPrimitive -> when (__input.jsonObject["${schema.discriminator}"]!!.jsonPrimitive.contentOrNull) {
490
+ ${subTypes.map((type) => `"${type.discriminatorValue}" -> ${type.typeName}.fromJsonElement(__input, instancePath)`).join("\n")}
491
+ else -> new()
492
+ }
493
+
494
+ else -> new()
495
+ }
496
+ }
497
+ }
498
+ }
499
+
500
+ ${subContent.join("\n\n")}`;
501
+ context.existingTypeIds.push(className);
502
+ return {
503
+ ...result,
504
+ content
505
+ };
506
+ }
507
+
508
+ function kotlinEnumFromSchema(schema, context) {
509
+ const nullable = isNullable(schema, context);
510
+ const className = getClassName(schema, context);
511
+ const enumItems = schema.enum.map((val) => {
512
+ return {
513
+ name: pascalCase(val, { normalize: true }),
514
+ value: val
515
+ };
516
+ });
517
+ if (!enumItems.length) {
518
+ throw new Error(
519
+ `Enum schemas must have at least one enum value. At ${context.schemaPath}.`
520
+ );
521
+ }
522
+ const defaultValue = nullable ? "null" : `${className}.new()`;
523
+ let content = "";
524
+ if (!context.existingTypeIds.includes(className)) {
525
+ content = `enum class ${className} {
526
+ ${enumItems.map((item) => item.name).join(",\n ")};
527
+ val serialValue: String
528
+ get() = when (this) {
529
+ ${enumItems.map((item) => `${item.name} -> "${item.value}"`).join("\n ")}
530
+ }
531
+
532
+ companion object Factory : ${context.clientName}ModelFactory<${className}> {
533
+ @JvmStatic
534
+ override fun new(): ${className} {
535
+ return ${enumItems[0].name}
536
+ }
537
+
538
+ @JvmStatic
539
+ override fun fromJson(input: String): ${className} {
540
+ return when (input) {
541
+ ${enumItems.map((item) => `${item.name}.serialValue -> ${item.name}`).join("\n ")}
542
+ else -> ${enumItems[0].name}
543
+ }
544
+ }
545
+
546
+ @JvmStatic
547
+ override fun fromJsonElement(__input: JsonElement, instancePath: String): ${className} {
548
+ if (__input !is JsonPrimitive) {
549
+ __logError("[WARNING] ${className}.fromJsonElement() expected kotlinx.serialization.json.JsonPrimitive at $instancePath. Got \${__input.javaClass}. Initializing empty ${className}.")
550
+ return new()
551
+ }
552
+ return when (__input.jsonPrimitive.contentOrNull) {
553
+ ${enumItems.map((item) => `"${item.value}" -> ${item.name}`).join("\n ")}
554
+ else -> new()
555
+ }
556
+ }
557
+ }
558
+ }`;
559
+ context.existingTypeIds.push(className);
560
+ }
561
+ return {
562
+ typeName: className,
563
+ isNullable: nullable,
564
+ defaultValue,
565
+ fromJson(input, key) {
566
+ if (nullable) {
567
+ return `when (${input}) {
568
+ is JsonNull -> null
569
+ is JsonPrimitive -> ${className}.fromJsonElement(${input}!!, "$instancePath/${key ?? ""}")
570
+ else -> null
571
+ }`;
572
+ }
573
+ return `when (${input}) {
574
+ is JsonNull -> ${defaultValue}
575
+ is JsonPrimitive -> ${className}.fromJsonElement(${input}!!, "$instancePath/${key}")
576
+ else -> ${defaultValue}
577
+ }`;
578
+ },
579
+ toJson(input, target) {
580
+ if (schema.nullable) {
581
+ return `${target} += when (${input}) {
582
+ is ${className} -> "\\"\${${input}.serialValue}\\""
583
+ else -> "null"
584
+ }`;
585
+ }
586
+ return `${target} += "\\"\${${input}.serialValue}\\""`;
587
+ },
588
+ toQueryString(input, target, key) {
589
+ if (context.isOptional) {
590
+ return `if (${input} != null) {
591
+ ${target}.add("${key}=\${${input}.serialValue}")
592
+ }`;
593
+ }
594
+ if (schema.nullable) {
595
+ return `${target}.add("${key}=\${${input}?.serialValue}")`;
596
+ }
597
+ return `${target}.add("${key}=\${${input}.serialValue}")`;
598
+ },
599
+ content
600
+ };
601
+ }
602
+
603
+ function kotlinMapFromSchema(schema, context) {
604
+ const nullable = isNullable(schema, context);
605
+ const subType = kotlinTypeFromSchema(schema.values, {
606
+ modelPrefix: context.modelPrefix,
607
+ clientName: context.clientName,
608
+ clientVersion: context.clientVersion,
609
+ instancePath: `${context.instancePath}/[value]`,
610
+ schemaPath: `${context.schemaPath}/values`,
611
+ existingTypeIds: context.existingTypeIds
612
+ });
613
+ const typeName = `MutableMap<String, ${subType.typeName}${subType.isNullable ? "?" : ""}>`;
614
+ const defaultValue = nullable ? "null" : "mutableMapOf()";
615
+ return {
616
+ typeName,
617
+ isNullable: nullable,
618
+ defaultValue,
619
+ fromJson(input, key) {
620
+ return `when (${input}) {
621
+ is JsonObject -> {
622
+ val __value: ${typeName} = mutableMapOf()
623
+ for (__entry in ${input}!!.jsonObject.entries) {
624
+ __value[__entry.key] = ${subType.fromJson("__entry.value", key)}
625
+ }
626
+ __value
627
+ }
628
+
629
+ else -> ${defaultValue}
630
+ }`;
631
+ },
632
+ toJson(input, target) {
633
+ if (schema.nullable) {
634
+ return `if (${input} == null) {
635
+ ${target} += "null"
636
+ } else {
637
+ ${target} += "{"
638
+ for ((__index, __entry) in ${input}.entries.withIndex()) {
639
+ if (__index != 0) {
640
+ ${target} += ","
641
+ }
642
+ ${target} += "\\"\${__entry.key}\\":"
643
+ ${subType.toJson("__entry.value", target)}
644
+ }
645
+ ${target} += "}"
646
+ }`;
647
+ }
648
+ return `${target} += "{"
649
+ for ((__index, __entry) in ${input}.entries.withIndex()) {
650
+ if (__index != 0) {
651
+ ${target} += ","
652
+ }
653
+ ${target} += "\\"\${__entry.key}\\":"
654
+ ${subType.toJson("__entry.value", target)}
655
+ }
656
+ ${target} += "}"`;
657
+ },
658
+ toQueryString() {
659
+ return `__logError("[WARNING] nested objects cannot be serialized to query params. Skipping field at ${context.instancePath}.")`;
660
+ },
661
+ content: subType.content
662
+ };
663
+ }
664
+
665
+ function defaultToQueryString(context, input, target, key) {
666
+ if (context.isOptional) {
667
+ return `if (${input} != null) {
668
+ ${target}.add("${key}=$${input}")
669
+ }`;
670
+ }
671
+ return `${target}.add("${key}=$${input}")`;
672
+ }
673
+ function defaultToJsonString(context, input, target) {
674
+ return `${target} += ${input}`;
675
+ }
676
+ function kotlinStringFromSchema(schema, context) {
677
+ const nullable = isNullable(schema, context);
678
+ const defaultValue = nullable ? "null" : '""';
679
+ return {
680
+ typeName: "String",
681
+ isNullable: nullable,
682
+ defaultValue,
683
+ fromJson(input) {
684
+ if (nullable) {
685
+ return `when (${input}) {
686
+ is JsonPrimitive -> ${input}!!.jsonPrimitive.contentOrNull
687
+ else -> null
688
+ }`;
689
+ }
690
+ return `when (${input}) {
691
+ is JsonPrimitive -> ${input}!!.jsonPrimitive.contentOrNull ?: ${defaultValue}
692
+ else -> ${defaultValue}
693
+ }`;
694
+ },
695
+ toJson(input, target) {
696
+ if (schema.nullable) {
697
+ return `${target} += when (${input}) {
698
+ is String -> buildString { printQuoted(${input}) }
699
+ else -> "null"
700
+ }`;
701
+ }
702
+ return `${target} += buildString { printQuoted(${input}) }`;
703
+ },
704
+ toQueryString(input, target, key) {
705
+ return defaultToQueryString(context, input, target, key);
706
+ },
707
+ content: ""
708
+ };
709
+ }
710
+ function kotlinBooleanFromSchema(schema, context) {
711
+ const nullable = isNullable(schema, context);
712
+ const defaultValue = nullable ? "null" : "false";
713
+ return {
714
+ typeName: "Boolean",
715
+ isNullable: nullable,
716
+ defaultValue,
717
+ fromJson(input) {
718
+ if (nullable) {
719
+ return `when (${input}) {
720
+ is JsonPrimitive -> ${input}!!.jsonPrimitive.booleanOrNull
721
+ else -> null
722
+ }`;
723
+ }
724
+ return `when (${input}) {
725
+ is JsonPrimitive -> ${input}!!.jsonPrimitive.booleanOrNull ?: ${defaultValue}
726
+ else -> ${defaultValue}
727
+ }`;
728
+ },
729
+ toJson(input, target) {
730
+ return defaultToJsonString(context, input, target);
731
+ },
732
+ toQueryString(input, target, key) {
733
+ return defaultToQueryString(context, input, target, key);
734
+ },
735
+ content: ""
736
+ };
737
+ }
738
+ function kotlinTimestampFromSchema(schema, context) {
739
+ const nullable = isNullable(schema, context);
740
+ const defaultValue = nullable ? "null" : "Instant.now()";
741
+ return {
742
+ typeName: "Instant",
743
+ isNullable: nullable,
744
+ defaultValue,
745
+ fromJson(input) {
746
+ return `when (${input}) {
747
+ is JsonPrimitive ->
748
+ if (${input}!!.jsonPrimitive.isString)
749
+ Instant.parse(${input}!!.jsonPrimitive.content)
750
+ else
751
+ ${defaultValue}
752
+ else -> ${defaultValue}
753
+ }`;
754
+ },
755
+ toJson(input, target) {
756
+ if (schema.nullable) {
757
+ return `${target} += when (${input}) {
758
+ is Instant -> "\\"\${timestampFormatter.format(${input})}\\""
759
+ else -> "null"
760
+ }`;
761
+ }
762
+ return `${target} += "\\"\${timestampFormatter.format(${input})}\\""`;
763
+ },
764
+ toQueryString(input, target, key) {
765
+ if (context.isOptional) {
766
+ return `if (${input} != null) {
767
+ ${target}.add(
768
+ "${key}=\${
769
+ timestampFormatter.format(${input})
770
+ }"
771
+ )
772
+ }`;
773
+ }
774
+ if (schema.nullable) {
775
+ return `${target}.add(
776
+ "${key}=\${
777
+ when (${input}) {
778
+ is Instant -> timestampFormatter.format(${input})
779
+ else -> "null"
780
+ }
781
+ }"
782
+ )`;
783
+ }
784
+ return `${target}.add(
785
+ "${key}=\${
786
+ timestampFormatter.format(${input})
787
+ }"
788
+ )`;
789
+ },
790
+ content: ""
791
+ };
792
+ }
793
+ function kotlinFloat32FromSchema(schema, context) {
794
+ const nullable = isNullable(schema, context);
795
+ const defaultValue = nullable ? "null" : "0.0F";
796
+ return {
797
+ typeName: "Float",
798
+ isNullable: nullable,
799
+ defaultValue,
800
+ fromJson(input) {
801
+ if (nullable) {
802
+ return `when (${input}) {
803
+ is JsonPrimitive -> ${input}!!.jsonPrimitive.floatOrNull
804
+ else -> null
805
+ }`;
806
+ }
807
+ return `when (${input}) {
808
+ is JsonPrimitive -> ${input}!!.jsonPrimitive.floatOrNull ?: ${defaultValue}
809
+ else -> ${defaultValue}
810
+ }`;
811
+ },
812
+ toJson(input, target) {
813
+ return defaultToJsonString(context, input, target);
814
+ },
815
+ toQueryString(input, target, key) {
816
+ return defaultToQueryString(context, input, target, key);
817
+ },
818
+ content: ""
819
+ };
820
+ }
821
+ function kotlinFloat64FromSchema(schema, context) {
822
+ const nullable = isNullable(schema, context);
823
+ const defaultValue = nullable ? "null" : "0.0";
824
+ return {
825
+ typeName: "Double",
826
+ isNullable: nullable,
827
+ defaultValue,
828
+ fromJson(input) {
829
+ if (nullable) {
830
+ return `when (${input}) {
831
+ is JsonPrimitive -> ${input}!!.jsonPrimitive.doubleOrNull
832
+ else -> null
833
+ }`;
834
+ }
835
+ return `when (${input}) {
836
+ is JsonPrimitive -> ${input}!!.jsonPrimitive.doubleOrNull ?: 0.0
837
+ else -> 0.0
838
+ }`;
839
+ },
840
+ toJson(input, target) {
841
+ return defaultToJsonString(context, input, target);
842
+ },
843
+ toQueryString(input, target, key) {
844
+ return defaultToQueryString(context, input, target, key);
845
+ },
846
+ content: ""
847
+ };
848
+ }
849
+ function kotlinInt8FromSchema(schema, context) {
850
+ const nullable = isNullable(schema, context);
851
+ const defaultValue = nullable ? "null" : "0";
852
+ return {
853
+ typeName: "Byte",
854
+ isNullable: nullable,
855
+ defaultValue,
856
+ fromJson(input) {
857
+ if (nullable) {
858
+ return `when (${input}) {
859
+ is JsonPrimitive -> ${input}!!.jsonPrimitive.contentOrNull?.toByteOrNull()
860
+ else -> null
861
+ }`;
862
+ }
863
+ return `when (${input}) {
864
+ is JsonPrimitive -> ${input}!!.jsonPrimitive.contentOrNull?.toByteOrNull() ?: 0
865
+ else -> 0
866
+ }`;
867
+ },
868
+ toJson(input, target) {
869
+ return defaultToJsonString(context, input, target);
870
+ },
871
+ toQueryString(input, target, key) {
872
+ return defaultToQueryString(context, input, target, key);
873
+ },
874
+ content: ""
875
+ };
876
+ }
877
+ function kotlinInt16FromSchema(schema, context) {
878
+ const nullable = isNullable(schema, context);
879
+ const defaultValue = nullable ? "null" : "0";
880
+ return {
881
+ typeName: "Short",
882
+ isNullable: nullable,
883
+ defaultValue,
884
+ fromJson(input) {
885
+ if (nullable) {
886
+ return `when (${input}) {
887
+ is JsonPrimitive -> ${input}!!.jsonPrimitive.contentOrNull?.toShortOrNull()
888
+ else -> null
889
+ }`;
890
+ }
891
+ return `when (${input}) {
892
+ is JsonPrimitive -> ${input}!!.jsonPrimitive.contentOrNull?.toShortOrNull() ?: 0
893
+ else -> 0
894
+ }`;
895
+ },
896
+ toJson(input, target) {
897
+ return defaultToJsonString(context, input, target);
898
+ },
899
+ toQueryString(input, target, key) {
900
+ return defaultToQueryString(context, input, target, key);
901
+ },
902
+ content: ""
903
+ };
904
+ }
905
+ function kotlinInt32FromSchema(schema, context) {
906
+ const nullable = isNullable(schema, context);
907
+ const defaultValue = nullable ? "null" : "0";
908
+ return {
909
+ typeName: "Int",
910
+ isNullable: nullable,
911
+ defaultValue,
912
+ fromJson(input) {
913
+ if (nullable) {
914
+ return `when (${input}) {
915
+ is JsonPrimitive -> ${input}!!.jsonPrimitive.intOrNull
916
+ else -> null
917
+ }`;
918
+ }
919
+ return `when (${input}) {
920
+ is JsonPrimitive -> ${input}!!.jsonPrimitive.intOrNull ?: 0
921
+ else -> 0
922
+ }`;
923
+ },
924
+ toJson(input, target) {
925
+ return defaultToJsonString(context, input, target);
926
+ },
927
+ toQueryString(input, target, key) {
928
+ return defaultToQueryString(context, input, target, key);
929
+ },
930
+ content: ""
931
+ };
932
+ }
933
+ function kotlinInt64FromSchema(schema, context) {
934
+ const nullable = isNullable(schema, context);
935
+ const defaultValue = nullable ? "null" : "0L";
936
+ return {
937
+ typeName: "Long",
938
+ isNullable: nullable,
939
+ defaultValue,
940
+ fromJson(input) {
941
+ if (nullable) {
942
+ return `when (${input}) {
943
+ is JsonPrimitive -> ${input}!!.jsonPrimitive.longOrNull
944
+ else -> null
945
+ }`;
946
+ }
947
+ return `when (${input}) {
948
+ is JsonPrimitive -> ${input}!!.jsonPrimitive.longOrNull ?: 0L
949
+ else -> 0L
950
+ }`;
951
+ },
952
+ toJson(input, target) {
953
+ if (schema.nullable) {
954
+ return `${target} += when (${input}) {
955
+ is Long -> "\\"$${input}\\""
956
+ else -> "null"
957
+ }`;
958
+ }
959
+ return `${target} += "\\"$${input}\\""`;
960
+ },
961
+ toQueryString(input, target, key) {
962
+ return defaultToQueryString(context, input, target, key);
963
+ },
964
+ content: ""
965
+ };
966
+ }
967
+ function kotlinUint8FromSchema(schema, context) {
968
+ const nullable = isNullable(schema, context);
969
+ const defaultValue = nullable ? "null" : "0u";
970
+ return {
971
+ typeName: "UByte",
972
+ isNullable: nullable,
973
+ defaultValue,
974
+ fromJson(input) {
975
+ if (nullable) {
976
+ return `when (${input}) {
977
+ is JsonPrimitive -> ${input}!!.jsonPrimitive.contentOrNull?.toUByteOrNull()
978
+ else -> null
979
+ }`;
980
+ }
981
+ return `when (${input}) {
982
+ is JsonPrimitive -> ${input}!!.jsonPrimitive.contentOrNull?.toUByteOrNull() ?: 0u
983
+ else -> 0u
984
+ }`;
985
+ },
986
+ toJson(input, target) {
987
+ return defaultToJsonString(context, input, target);
988
+ },
989
+ toQueryString(input, target, key) {
990
+ return defaultToQueryString(context, input, target, key);
991
+ },
992
+ content: ""
993
+ };
994
+ }
995
+ function kotlinUint16FromSchema(schema, context) {
996
+ const nullable = isNullable(schema, context);
997
+ const defaultValue = nullable ? "null" : "0u";
998
+ return {
999
+ typeName: "UShort",
1000
+ isNullable: nullable,
1001
+ defaultValue,
1002
+ fromJson(input) {
1003
+ if (nullable) {
1004
+ return `when (${input}) {
1005
+ is JsonPrimitive -> ${input}!!.jsonPrimitive.contentOrNull?.toUShortOrNull()
1006
+ else -> null
1007
+ }`;
1008
+ }
1009
+ return `when (${input}) {
1010
+ is JsonPrimitive -> ${input}!!.jsonPrimitive.contentOrNull?.toUShortOrNull() ?: 0u
1011
+ else -> 0u
1012
+ }`;
1013
+ },
1014
+ toJson(input, target) {
1015
+ return defaultToJsonString(context, input, target);
1016
+ },
1017
+ toQueryString(input, target, key) {
1018
+ return defaultToQueryString(context, input, target, key);
1019
+ },
1020
+ content: ""
1021
+ };
1022
+ }
1023
+ function kotlinUint32FromSchema(schema, context) {
1024
+ const nullable = isNullable(schema, context);
1025
+ const defaultValue = nullable ? "null" : "0u";
1026
+ return {
1027
+ typeName: "UInt",
1028
+ isNullable: nullable,
1029
+ defaultValue,
1030
+ fromJson(input) {
1031
+ if (nullable) {
1032
+ return `when (${input}) {
1033
+ is JsonPrimitive -> ${input}!!.jsonPrimitive.contentOrNull?.toUIntOrNull()
1034
+ else -> null
1035
+ }`;
1036
+ }
1037
+ return `when (${input}) {
1038
+ is JsonPrimitive -> ${input}!!.jsonPrimitive.contentOrNull?.toUIntOrNull() ?: 0u
1039
+ else -> 0u
1040
+ }`;
1041
+ },
1042
+ toJson(input, target) {
1043
+ return defaultToJsonString(context, input, target);
1044
+ },
1045
+ toQueryString(input, target, key) {
1046
+ return defaultToQueryString(context, input, target, key);
1047
+ },
1048
+ content: ""
1049
+ };
1050
+ }
1051
+ function kotlinUint64FromSchema(schema, context) {
1052
+ const nullable = isNullable(schema, context);
1053
+ const defaultValue = nullable ? "null" : "0UL";
1054
+ return {
1055
+ typeName: "ULong",
1056
+ isNullable: nullable,
1057
+ defaultValue,
1058
+ fromJson(input) {
1059
+ if (nullable) {
1060
+ return `when (${input}) {
1061
+ is JsonPrimitive -> ${input}!!.jsonPrimitive.contentOrNull?.toULongOrNull()
1062
+ else -> null
1063
+ }`;
1064
+ }
1065
+ return `when (${input}) {
1066
+ is JsonPrimitive -> ${input}!!.jsonPrimitive.contentOrNull?.toULongOrNull() ?: 0UL
1067
+ else -> 0UL
1068
+ }`;
1069
+ },
1070
+ toJson(input, target) {
1071
+ if (schema.nullable) {
1072
+ return `${target} += when (${input}) {
1073
+ is ULong -> "\\"$${input}\\""
1074
+ else -> "null"
1075
+ }`;
1076
+ }
1077
+ return `${target} += "\\"$${input}\\""`;
1078
+ },
1079
+ toQueryString(input, target, key) {
1080
+ return defaultToQueryString(context, input, target, key);
1081
+ },
1082
+ content: ""
1083
+ };
1084
+ }
1085
+
1086
+ function kotlinProcedureFromSchema(schema, context) {
1087
+ switch (schema.transport) {
1088
+ case "http":
1089
+ return kotlinHttpRpcFromSchema(schema, context);
1090
+ case "ws":
1091
+ return kotlinWsRpcFromSchema();
1092
+ default:
1093
+ console.warn(
1094
+ `[codegen-kotlin] Unknown transport type "${schema.transport}". Skipping "${context.instancePath}".`
1095
+ );
1096
+ return "";
1097
+ }
1098
+ }
1099
+ function kotlinHttpRpcFromSchema(schema, context) {
1100
+ const name = getProcedureName(context);
1101
+ const params = schema.params ? kotlinClassName(`${context.modelPrefix}_${schema.params}`) : void 0;
1102
+ const response = schema.response ? kotlinClassName(`${context.modelPrefix}_${schema.response}`) : void 0;
1103
+ if (schema.isEventStream) {
1104
+ return `fun ${name}(
1105
+ scope: CoroutineScope,
1106
+ ${params ? `params: ${params},` : ""}
1107
+ lastEventId: String? = null,
1108
+ bufferCapacity: Int = 1024,
1109
+ onOpen: ((response: HttpResponse) -> Unit) = {},
1110
+ onClose: (() -> Unit) = {},
1111
+ onError: ((error: ${context.clientName}Error) -> Unit) = {},
1112
+ onConnectionError: ((error: ${context.clientName}Error) -> Unit) = {},
1113
+ onData: ((${response ? `data: ${response}` : ""}) -> Unit) = {},
1114
+ ): Job {
1115
+ val job = scope.launch {
1116
+ __handleSseRequest(
1117
+ scope = scope,
1118
+ httpClient = httpClient,
1119
+ url = "$baseUrl${schema.path}",
1120
+ method = HttpMethod.${pascalCase(schema.method, { normalize: true })},
1121
+ params = ${params ? "params" : "null"},
1122
+ headers = headers,
1123
+ backoffTime = 0,
1124
+ maxBackoffTime = 30000L,
1125
+ lastEventId = lastEventId,
1126
+ bufferCapacity = bufferCapacity,
1127
+ onOpen = onOpen,
1128
+ onClose = onClose,
1129
+ onError = onError,
1130
+ onConnectionError = onConnectionError,
1131
+ onData = { str ->
1132
+ ${response ? `val data = ${response}.fromJson(str)` : ""}
1133
+ onData(${response ? "data" : ""})
1134
+ }
1135
+ )
1136
+ }
1137
+ return job
1138
+ }`;
1139
+ }
1140
+ const headingCheck = `if (response.headers["Content-Type"] != "application/json") {
1141
+ throw ${context.clientName}Error(
1142
+ code = 0,
1143
+ errorMessage = "Expected server to return Content-Type \\"application/json\\". Got \\"\${response.headers["Content-Type"]}\\"",
1144
+ data = JsonPrimitive(response.bodyAsText()),
1145
+ stack = null,
1146
+ )
1147
+ }`;
1148
+ return `suspend fun ${name}(${params ? `params: ${params}` : ""}): ${response ?? "Unit"} {
1149
+ val response = __prepareRequest(
1150
+ client = httpClient,
1151
+ url = "$baseUrl${schema.path}",
1152
+ method = HttpMethod.${pascalCase(schema.method, { normalize: true })},
1153
+ params = ${params ? "params" : null},
1154
+ headers = headers?.invoke(),
1155
+ ).execute()
1156
+ ${response ? headingCheck : ""}
1157
+ if (response.status.value in 200..299) {
1158
+ return ${response ? `${response}.fromJson(response.bodyAsText())` : ""}
1159
+ }
1160
+ throw ${context.clientName}Error.fromJson(response.bodyAsText())
1161
+ }`;
1162
+ }
1163
+ function kotlinWsRpcFromSchema(schema, context) {
1164
+ return "";
1165
+ }
1166
+ function kotlinServiceFromSchema(schema, context) {
1167
+ const name = getServiceName(context);
1168
+ const procedureParts = [];
1169
+ const subServiceParts = [];
1170
+ for (const key of Object.keys(schema)) {
1171
+ const kotlinKey = kotlinIdentifier(key);
1172
+ const subSchema = schema[key];
1173
+ if (isServiceDefinition(subSchema)) {
1174
+ const subService = kotlinServiceFromSchema(subSchema, {
1175
+ ...context,
1176
+ instancePath: `${context.instancePath}.${key}`
1177
+ });
1178
+ procedureParts.push(`val ${kotlinKey}: ${subService.name} = ${subService.name}(
1179
+ httpClient = httpClient,
1180
+ baseUrl = baseUrl,
1181
+ headers = headers,
1182
+ )`);
1183
+ if (subService.content) {
1184
+ subServiceParts.push(subService.content);
1185
+ }
1186
+ continue;
1187
+ }
1188
+ if (isRpcDefinition(subSchema)) {
1189
+ const procedure = kotlinProcedureFromSchema(subSchema, {
1190
+ ...context,
1191
+ instancePath: `${context.instancePath}.${key}`
1192
+ });
1193
+ if (procedure.length > 0) {
1194
+ procedureParts.push(procedure);
1195
+ }
1196
+ continue;
1197
+ }
1198
+ }
1199
+ return {
1200
+ name,
1201
+ content: `class ${name}(
1202
+ private val httpClient: HttpClient,
1203
+ private val baseUrl: String,
1204
+ private val headers: headersFn,
1205
+ ) {
1206
+ ${procedureParts.join("\n\n ")}
1207
+ }
1208
+
1209
+ ${subServiceParts.join("\n\n")}`
1210
+ };
1211
+ }
1212
+ function getProcedureName(context) {
1213
+ const name = context.instancePath.split(".").pop() ?? "";
1214
+ return kotlinIdentifier(name);
1215
+ }
1216
+ function getServiceName(context) {
1217
+ const name = pascalCase(context.instancePath.split(".").join("_"));
1218
+ return `${context.clientName}${name}Service`;
1219
+ }
1220
+
1221
+ function kotlinRefFromSchema(schema, context) {
1222
+ const typeName = kotlinClassName(schema.ref);
1223
+ const nullable = isNullable(schema, context);
1224
+ const defaultValue = nullable ? "null" : `${typeName}.new()`;
1225
+ return {
1226
+ typeName,
1227
+ isNullable: nullable,
1228
+ defaultValue,
1229
+ fromJson(input, key) {
1230
+ return `when (${input}) {
1231
+ is JsonObject -> ${typeName}.fromJsonElement(
1232
+ ${input}!!,
1233
+ "$instancePath/${key}",
1234
+ )
1235
+ else -> ${defaultValue}
1236
+ }`;
1237
+ },
1238
+ toJson(input, target) {
1239
+ if (schema.nullable) {
1240
+ return `${target} += ${input}?.toJson()`;
1241
+ }
1242
+ return `${target} += ${input}.toJson()`;
1243
+ },
1244
+ toQueryString() {
1245
+ return `__logError("[WARNING] nested objects cannot be serialized to query params. Skipping field at ${context.instancePath}.")`;
1246
+ },
1247
+ content: ""
1248
+ };
1249
+ }
1250
+
1251
+ const kotlinClientGenerator = defineClientGeneratorPlugin(
1252
+ (options) => {
1253
+ return {
1254
+ generator(def) {
1255
+ const client = kotlinClientFromAppDefinition(def, options);
1256
+ fs.writeFileSync(options.outputFile, client);
1257
+ },
1258
+ options
1259
+ };
1260
+ }
1261
+ );
1262
+ function kotlinClientFromAppDefinition(def, options) {
1263
+ const clientName = kotlinClassName(options.clientName ?? "Client");
1264
+ const context = {
1265
+ modelPrefix: options.modelPrefix ?? "",
1266
+ clientName,
1267
+ clientVersion: def.info?.version ?? "",
1268
+ instancePath: "",
1269
+ schemaPath: "",
1270
+ existingTypeIds: []
1271
+ };
1272
+ const modelParts = [];
1273
+ for (const key of Object.keys(def.definitions)) {
1274
+ const subSchema = def.definitions[key];
1275
+ const model = kotlinTypeFromSchema(subSchema, {
1276
+ modelPrefix: context.modelPrefix,
1277
+ clientName: context.clientName,
1278
+ clientVersion: context.clientVersion,
1279
+ instancePath: `/${key}`,
1280
+ schemaPath: `/definitions`,
1281
+ existingTypeIds: context.existingTypeIds
1282
+ });
1283
+ if (model.content) {
1284
+ modelParts.push(model.content);
1285
+ }
1286
+ }
1287
+ const procedureParts = [];
1288
+ const subServiceParts = [];
1289
+ const services = unflattenProcedures(def.procedures);
1290
+ for (const key of Object.keys(services)) {
1291
+ const subSchema = services[key];
1292
+ if (isServiceDefinition(subSchema)) {
1293
+ const kotlinKey = kotlinIdentifier(key);
1294
+ const subService = kotlinServiceFromSchema(subSchema, {
1295
+ ...context,
1296
+ instancePath: `${key}`
1297
+ });
1298
+ procedureParts.push(`val ${kotlinKey}: ${subService.name} = ${subService.name}(
1299
+ httpClient = httpClient,
1300
+ baseUrl = baseUrl,
1301
+ headers = headers,
1302
+ )`);
1303
+ if (subService.content) {
1304
+ subServiceParts.push(subService.content);
1305
+ }
1306
+ continue;
1307
+ }
1308
+ if (isRpcDefinition(subSchema)) {
1309
+ const procedure = kotlinProcedureFromSchema(subSchema, {
1310
+ ...context,
1311
+ instancePath: key
1312
+ });
1313
+ if (procedure.length) {
1314
+ procedureParts.push(procedure);
1315
+ }
1316
+ continue;
1317
+ }
1318
+ }
1319
+ if (procedureParts.length === 0) {
1320
+ return `${getHeader({ clientName, clientVersion: context.clientVersion, packageName: "" })}
1321
+ ${getUtilityClasses(clientName)}
1322
+
1323
+ ${modelParts.join("\n\n")}
1324
+
1325
+ ${getUtilityFunctions(clientName)}`;
1326
+ }
1327
+ return `${getHeader({ clientName, clientVersion: context.clientVersion, packageName: "" })}
1328
+
1329
+ class ${clientName}(
1330
+ private val httpClient: HttpClient,
1331
+ private val baseUrl: String,
1332
+ private val headers: headersFn,
1333
+ ) {
1334
+ ${procedureParts.join("\n\n ")}
1335
+ }
1336
+
1337
+ ${subServiceParts.join("\n\n")}
1338
+
1339
+ ${getUtilityClasses(clientName)}
1340
+
1341
+ ${modelParts.join("\n\n")}
1342
+
1343
+ ${getUtilityFunctions(clientName)}`;
1344
+ }
1345
+ function kotlinTypeFromSchema(schema, context) {
1346
+ if (isSchemaFormType(schema)) {
1347
+ switch (schema.type) {
1348
+ case "string":
1349
+ return kotlinStringFromSchema(schema, context);
1350
+ case "boolean":
1351
+ return kotlinBooleanFromSchema(schema, context);
1352
+ case "timestamp":
1353
+ return kotlinTimestampFromSchema(schema, context);
1354
+ case "float32":
1355
+ return kotlinFloat32FromSchema(schema, context);
1356
+ case "float64":
1357
+ return kotlinFloat64FromSchema(schema, context);
1358
+ case "int8":
1359
+ return kotlinInt8FromSchema(schema, context);
1360
+ case "int16":
1361
+ return kotlinInt16FromSchema(schema, context);
1362
+ case "int32":
1363
+ return kotlinInt32FromSchema(schema, context);
1364
+ case "int64":
1365
+ return kotlinInt64FromSchema(schema, context);
1366
+ case "uint8":
1367
+ return kotlinUint8FromSchema(schema, context);
1368
+ case "uint16":
1369
+ return kotlinUint16FromSchema(schema, context);
1370
+ case "uint32":
1371
+ return kotlinUint32FromSchema(schema, context);
1372
+ case "uint64":
1373
+ return kotlinUint64FromSchema(schema, context);
1374
+ default:
1375
+ schema.type;
1376
+ throw new Error(`Unhandled schema.type case`);
1377
+ }
1378
+ }
1379
+ if (isSchemaFormEnum(schema)) {
1380
+ return kotlinEnumFromSchema(schema, context);
1381
+ }
1382
+ if (isSchemaFormProperties(schema)) {
1383
+ return kotlinObjectFromSchema(schema, context);
1384
+ }
1385
+ if (isSchemaFormElements(schema)) {
1386
+ return kotlinArrayFromSchema(schema, context);
1387
+ }
1388
+ if (isSchemaFormValues(schema)) {
1389
+ return kotlinMapFromSchema(schema, context);
1390
+ }
1391
+ if (isSchemaFormDiscriminator(schema)) {
1392
+ return kotlinDiscriminatorFromSchema(schema, context);
1393
+ }
1394
+ if (isSchemaFormRef(schema)) {
1395
+ return kotlinRefFromSchema(schema, context);
1396
+ }
1397
+ return kotlinAnyFromSchema(schema, context);
1398
+ }
1399
+ function getUtilityClasses(clientName) {
1400
+ return `interface ${clientName}Model {
1401
+ fun toJson(): String
1402
+ fun toUrlQueryParams(): String
1403
+ }
1404
+
1405
+ interface ${clientName}ModelFactory<T> {
1406
+ fun new(): T
1407
+ fun fromJson(input: String): T
1408
+ fun fromJsonElement(
1409
+ __input: JsonElement,
1410
+ instancePath: String = "",
1411
+ ): T
1412
+ }
1413
+
1414
+ data class ${clientName}Error(
1415
+ val code: Int,
1416
+ val errorMessage: String,
1417
+ val data: JsonElement?,
1418
+ val stack: List<String>?,
1419
+ ) : Exception(errorMessage), ${clientName}Model {
1420
+ override fun toJson(): String {
1421
+ var output = "{"
1422
+ output += "\\"code\\":"
1423
+ output += "$code"
1424
+ output += ",\\"message\\":"
1425
+ output += buildString { printQuoted(errorMessage) }
1426
+ if (data != null) {
1427
+ output += ",\\"data\\":"
1428
+ output += JsonInstance.encodeToString(data)
1429
+ }
1430
+ if (stack != null) {
1431
+ output += ",\\"stack\\":"
1432
+ output += "["
1433
+ for ((__index, __element) in stack.withIndex()) {
1434
+ if (__index > 0) {
1435
+ output += ","
1436
+ }
1437
+ output += buildString { printQuoted(__element) }
1438
+ }
1439
+ output += "]"
1440
+ }
1441
+ output += "}"
1442
+ return output
1443
+ }
1444
+
1445
+ override fun toUrlQueryParams(): String {
1446
+ val queryParts = mutableListOf<String>()
1447
+ queryParts.add("code=\${code}")
1448
+ queryParts.add("message=\${errorMessage}")
1449
+ return queryParts.joinToString("&")
1450
+ }
1451
+
1452
+ companion object Factory : ${clientName}ModelFactory<${clientName}Error> {
1453
+ override fun new(): ${clientName}Error {
1454
+ return ${clientName}Error(
1455
+ code = 0,
1456
+ errorMessage = "",
1457
+ data = null,
1458
+ stack = null
1459
+ )
1460
+ }
1461
+
1462
+ override fun fromJson(input: String): ${clientName}Error {
1463
+ return fromJsonElement(JsonInstance.parseToJsonElement(input))
1464
+ }
1465
+
1466
+ override fun fromJsonElement(__input: JsonElement, instancePath: String): ${clientName}Error {
1467
+ if (__input !is JsonObject) {
1468
+ __logError("[WARNING] ${clientName}Error.fromJsonElement() expected JsonObject at $instancePath. Got \${__input.javaClass}. Initializing empty ${clientName}Error.")
1469
+ }
1470
+ val code = when (__input.jsonObject["code"]) {
1471
+ is JsonPrimitive -> __input.jsonObject["code"]!!.jsonPrimitive.intOrNull ?: 0
1472
+ else -> 0
1473
+ }
1474
+ val errorMessage = when (__input.jsonObject["message"]) {
1475
+ is JsonPrimitive -> __input.jsonObject["message"]!!.jsonPrimitive.contentOrNull ?: ""
1476
+ else -> ""
1477
+ }
1478
+ val data = when (__input.jsonObject["data"]) {
1479
+ is JsonNull -> null
1480
+ is JsonElement -> __input.jsonObject["data"]!!
1481
+ else -> null
1482
+ }
1483
+ val stack = when (__input.jsonObject["stack"]) {
1484
+ is JsonArray -> {
1485
+ val stackVal = mutableListOf<String>()
1486
+ for (item in __input.jsonObject["stack"]!!.jsonArray) {
1487
+ stackVal.add(
1488
+ when (item) {
1489
+ is JsonPrimitive -> item.contentOrNull ?: ""
1490
+ else -> ""
1491
+ }
1492
+ )
1493
+ }
1494
+ stackVal
1495
+ }
1496
+
1497
+ else -> null
1498
+
1499
+ }
1500
+ return ${clientName}Error(
1501
+ code,
1502
+ errorMessage,
1503
+ data,
1504
+ stack,
1505
+ )
1506
+ }
1507
+
1508
+ }
1509
+ }`;
1510
+ }
1511
+ function getUtilityFunctions(clientName) {
1512
+ return `// Implementation copied from https://github.com/Kotlin/kotlinx.serialization/blob/d0ae697b9394103879e6c7f836d0f7cf128f4b1e/formats/json/commonMain/src/kotlinx/serialization/json/internal/StringOps.kt#L45
1513
+ internal const val STRING = '"'
1514
+
1515
+ private fun toHexChar(i: Int): Char {
1516
+ val d = i and 0xf
1517
+ return if (d < 10) (d + '0'.code).toChar()
1518
+ else (d - 10 + 'a'.code).toChar()
1519
+ }
1520
+
1521
+ internal val ESCAPE_STRINGS: Array<String?> = arrayOfNulls<String>(93).apply {
1522
+ for (c in 0..0x1f) {
1523
+ val c1 = toHexChar(c shr 12)
1524
+ val c2 = toHexChar(c shr 8)
1525
+ val c3 = toHexChar(c shr 4)
1526
+ val c4 = toHexChar(c)
1527
+ this[c] = "\\\\u$c1$c2$c3$c4"
1528
+ }
1529
+ this['"'.code] = "\\\\\\""
1530
+ this['\\\\'.code] = "\\\\\\\\"
1531
+ this['\\t'.code] = "\\\\t"
1532
+ this['\\b'.code] = "\\\\b"
1533
+ this['\\n'.code] = "\\\\n"
1534
+ this['\\r'.code] = "\\\\r"
1535
+ this[0x0c] = "\\\\f"
1536
+ }
1537
+
1538
+ internal val ESCAPE_MARKERS: ByteArray = ByteArray(93).apply {
1539
+ for (c in 0..0x1f) {
1540
+ this[c] = 1.toByte()
1541
+ }
1542
+ this['"'.code] = '"'.code.toByte()
1543
+ this['\\\\'.code] = '\\\\'.code.toByte()
1544
+ this['\\t'.code] = 't'.code.toByte()
1545
+ this['\\b'.code] = 'b'.code.toByte()
1546
+ this['\\n'.code] = 'n'.code.toByte()
1547
+ this['\\r'.code] = 'r'.code.toByte()
1548
+ this[0x0c] = 'f'.code.toByte()
1549
+ }
1550
+
1551
+ internal fun StringBuilder.printQuoted(value: String) {
1552
+ append(STRING)
1553
+ var lastPos = 0
1554
+ for (i in value.indices) {
1555
+ val c = value[i].code
1556
+ if (c < ESCAPE_STRINGS.size && ESCAPE_STRINGS[c] != null) {
1557
+ append(value, lastPos, i) // flush prev
1558
+ append(ESCAPE_STRINGS[c])
1559
+ lastPos = i + 1
1560
+ }
1561
+ }
1562
+
1563
+ if (lastPos != 0) append(value, lastPos, value.length)
1564
+ else append(value)
1565
+ append(STRING)
1566
+ }
1567
+
1568
+ private fun __logError(string: String) {
1569
+ System.err.println(string)
1570
+ }
1571
+
1572
+ private suspend fun __prepareRequest(
1573
+ client: HttpClient,
1574
+ url: String,
1575
+ method: HttpMethod,
1576
+ params: ${clientName}Model?,
1577
+ headers: MutableMap<String, String>?,
1578
+ ): HttpStatement {
1579
+ var finalUrl = url
1580
+ var finalBody = ""
1581
+ when (method) {
1582
+ HttpMethod.Get, HttpMethod.Head -> {
1583
+ finalUrl = "$finalUrl?\${params?.toUrlQueryParams() ?: ""}"
1584
+ }
1585
+
1586
+ HttpMethod.Post, HttpMethod.Put, HttpMethod.Patch, HttpMethod.Delete -> {
1587
+ finalBody = params?.toJson() ?: ""
1588
+ }
1589
+ }
1590
+ val builder = HttpRequestBuilder()
1591
+ builder.method = method
1592
+ builder.url(finalUrl)
1593
+ builder.timeout {
1594
+ requestTimeoutMillis = 10 * 60 * 1000
1595
+ }
1596
+ if (headers != null) {
1597
+ for (entry in headers.entries) {
1598
+ builder.headers[entry.key] = entry.value
1599
+ }
1600
+ }
1601
+ builder.headers["client-version"] = generatedClientVersion
1602
+ if (method != HttpMethod.Get && method != HttpMethod.Head) {
1603
+ builder.setBody(finalBody)
1604
+ }
1605
+ return client.prepareRequest(builder)
1606
+ }
1607
+
1608
+ private fun __parseSseEvent(input: String): __SseEvent {
1609
+ val lines = input.split("\\n")
1610
+ var id: String? = null
1611
+ var event: String? = null
1612
+ var data: String = ""
1613
+ for (line in lines) {
1614
+ if (line.startsWith("id: ")) {
1615
+ id = line.substring(3).trim()
1616
+ continue
1617
+ }
1618
+ if (line.startsWith("event: ")) {
1619
+ event = line.substring(6).trim()
1620
+ continue
1621
+ }
1622
+ if (line.startsWith("data: ")) {
1623
+ data = line.substring(5).trim()
1624
+ continue
1625
+ }
1626
+ }
1627
+ return __SseEvent(id, event, data)
1628
+ }
1629
+
1630
+ private class __SseEvent(val id: String? = null, val event: String? = null, val data: String)
1631
+
1632
+ private class __SseEventParsingResult(val events: List<__SseEvent>, val leftover: String)
1633
+
1634
+ private fun __parseSseEvents(input: String): __SseEventParsingResult {
1635
+ val inputs = input.split("\\n\\n").toMutableList()
1636
+ if (inputs.isEmpty()) {
1637
+ return __SseEventParsingResult(
1638
+ events = listOf(),
1639
+ leftover = "",
1640
+ )
1641
+ }
1642
+ if (inputs.size == 1) {
1643
+ return __SseEventParsingResult(
1644
+ events = listOf(),
1645
+ leftover = inputs.last(),
1646
+ )
1647
+ }
1648
+ val leftover = inputs.last()
1649
+ inputs.removeLast()
1650
+ val events = mutableListOf<__SseEvent>()
1651
+ for (item in inputs) {
1652
+ if (item.contains("data: ")) {
1653
+ events.add(__parseSseEvent(item))
1654
+ }
1655
+ }
1656
+ return __SseEventParsingResult(
1657
+ events = events,
1658
+ leftover = leftover,
1659
+ )
1660
+ }
1661
+
1662
+ private suspend fun __handleSseRequest(
1663
+ scope: CoroutineScope,
1664
+ httpClient: HttpClient,
1665
+ url: String,
1666
+ method: HttpMethod,
1667
+ params: ${clientName}Model?,
1668
+ headers: headersFn,
1669
+ backoffTime: Long,
1670
+ maxBackoffTime: Long,
1671
+ lastEventId: String?,
1672
+ onOpen: ((response: HttpResponse) -> Unit) = {},
1673
+ onClose: (() -> Unit) = {},
1674
+ onError: ((error: ${clientName}Error) -> Unit) = {},
1675
+ onData: ((data: String) -> Unit) = {},
1676
+ onConnectionError: ((error: ${clientName}Error) -> Unit) = {},
1677
+ bufferCapacity: Int,
1678
+ ) {
1679
+ val finalHeaders = headers?.invoke() ?: mutableMapOf()
1680
+ var lastId = lastEventId
1681
+ // exponential backoff maxing out at 32 seconds
1682
+ if (backoffTime > 0) {
1683
+ withContext(scope.coroutineContext) {
1684
+ Thread.sleep(backoffTime)
1685
+ }
1686
+ }
1687
+ var newBackoffTime =
1688
+ if (backoffTime == 0L) 2L else if (backoffTime * 2L >= maxBackoffTime) maxBackoffTime else backoffTime * 2L
1689
+ if (lastId != null) {
1690
+ finalHeaders["Last-Event-ID"] = lastId.toString()
1691
+ }
1692
+ val request = __prepareRequest(
1693
+ client = httpClient,
1694
+ url = url,
1695
+ method = method,
1696
+ params = params,
1697
+ headers = finalHeaders,
1698
+ )
1699
+ try {
1700
+ request.execute { httpResponse ->
1701
+ try {
1702
+ onOpen(httpResponse)
1703
+ } catch (e: CancellationException) {
1704
+ onClose()
1705
+ return@execute
1706
+ }
1707
+ if (httpResponse.status.value !in 200..299) {
1708
+ try {
1709
+ if (httpResponse.headers["Content-Type"] == "application/json") {
1710
+ onConnectionError(
1711
+ ${clientName}Error.fromJson(httpResponse.bodyAsText())
1712
+ )
1713
+ } else {
1714
+ onConnectionError(
1715
+ ${clientName}Error(
1716
+ code = httpResponse.status.value,
1717
+ errorMessage = httpResponse.status.description,
1718
+ data = JsonPrimitive(httpResponse.bodyAsText()),
1719
+ stack = null,
1720
+ )
1721
+ )
1722
+ }
1723
+ } catch (e: CancellationException) {
1724
+ onClose()
1725
+ return@execute
1726
+ }
1727
+ __handleSseRequest(
1728
+ scope = scope,
1729
+ httpClient = httpClient,
1730
+ url = url,
1731
+ method = method,
1732
+ params = params,
1733
+ headers = headers,
1734
+ backoffTime = newBackoffTime,
1735
+ maxBackoffTime = maxBackoffTime,
1736
+ lastEventId = lastId,
1737
+ bufferCapacity = bufferCapacity,
1738
+ onOpen = onOpen,
1739
+ onClose = onClose,
1740
+ onError = onError,
1741
+ onData = onData,
1742
+ onConnectionError = onConnectionError,
1743
+ )
1744
+ return@execute
1745
+ }
1746
+ if (httpResponse.headers["Content-Type"] != "text/event-stream") {
1747
+ try {
1748
+ onConnectionError(
1749
+ ${clientName}Error(
1750
+ code = 0,
1751
+ errorMessage = "Expected server to return Content-Type \\"text/event-stream\\". Got \\"\${httpResponse.headers["Content-Type"]}\\"",
1752
+ data = JsonPrimitive(httpResponse.bodyAsText()),
1753
+ stack = null,
1754
+ )
1755
+ )
1756
+ } catch (e: CancellationException) {
1757
+ return@execute
1758
+ }
1759
+ __handleSseRequest(
1760
+ scope = scope,
1761
+ httpClient = httpClient,
1762
+ url = url,
1763
+ method = method,
1764
+ params = params,
1765
+ headers = headers,
1766
+ backoffTime = newBackoffTime,
1767
+ maxBackoffTime = maxBackoffTime,
1768
+ lastEventId = lastId,
1769
+ bufferCapacity = bufferCapacity,
1770
+ onOpen = onOpen,
1771
+ onClose = onClose,
1772
+ onError = onError,
1773
+ onData = onData,
1774
+ onConnectionError = onConnectionError,
1775
+ )
1776
+ return@execute
1777
+ }
1778
+ newBackoffTime = 0
1779
+ val channel: ByteReadChannel = httpResponse.bodyAsChannel()
1780
+ var pendingData = ""
1781
+ while (!channel.isClosedForRead) {
1782
+ val buffer = ByteBuffer.allocateDirect(bufferCapacity)
1783
+ val read = channel.readAvailable(buffer)
1784
+ if (read == -1) break
1785
+ buffer.flip()
1786
+ val input = Charsets.UTF_8.decode(buffer).toString()
1787
+ val parsedResult = __parseSseEvents("\${pendingData}\${input}")
1788
+ pendingData = parsedResult.leftover
1789
+ for (event in parsedResult.events) {
1790
+ if (event.id != null) {
1791
+ lastId = event.id
1792
+ }
1793
+ when (event.event) {
1794
+ "message" -> {
1795
+ try {
1796
+ onData(event.data)
1797
+ } catch (e: CancellationException) {
1798
+ onClose()
1799
+ return@execute
1800
+ }
1801
+ }
1802
+
1803
+ "done" -> {
1804
+ onClose()
1805
+ return@execute
1806
+ }
1807
+
1808
+ "error" -> {
1809
+ val error = ${clientName}Error.fromJson(event.data)
1810
+ try {
1811
+ onError(error)
1812
+ } catch (e: CancellationException) {
1813
+ onClose()
1814
+ return@execute
1815
+ }
1816
+ }
1817
+
1818
+ else -> {}
1819
+ }
1820
+ }
1821
+ }
1822
+ __handleSseRequest(
1823
+ scope = scope,
1824
+ httpClient = httpClient,
1825
+ url = url,
1826
+ method = method,
1827
+ params = params,
1828
+ headers = headers,
1829
+ backoffTime = newBackoffTime,
1830
+ maxBackoffTime = maxBackoffTime,
1831
+ lastEventId = lastId,
1832
+ bufferCapacity = bufferCapacity,
1833
+ onOpen = onOpen,
1834
+ onClose = onClose,
1835
+ onError = onError,
1836
+ onData = onData,
1837
+ onConnectionError = onConnectionError,
1838
+ )
1839
+ }
1840
+ } catch (e: java.net.ConnectException) {
1841
+ onConnectionError(
1842
+ ${clientName}Error(
1843
+ code = 503,
1844
+ errorMessage = if (e.message != null) e.message!! else "Error connecting to $url",
1845
+ data = JsonPrimitive(e.toString()),
1846
+ stack = e.stackTraceToString().split("\\n"),
1847
+ )
1848
+ )
1849
+ __handleSseRequest(
1850
+ scope = scope,
1851
+ httpClient = httpClient,
1852
+ url = url,
1853
+ method = method,
1854
+ params = params,
1855
+ headers = headers,
1856
+ backoffTime = newBackoffTime,
1857
+ maxBackoffTime = maxBackoffTime,
1858
+ lastEventId = lastId,
1859
+ bufferCapacity = bufferCapacity,
1860
+ onOpen = onOpen,
1861
+ onClose = onClose,
1862
+ onError = onError,
1863
+ onData = onData,
1864
+ onConnectionError = onConnectionError,
1865
+ )
1866
+ return
1867
+ } catch (e: Exception) {
1868
+ __handleSseRequest(
1869
+ scope = scope,
1870
+ httpClient = httpClient,
1871
+ url = url,
1872
+ method = method,
1873
+ params = params,
1874
+ headers = headers,
1875
+ backoffTime = newBackoffTime,
1876
+ maxBackoffTime = maxBackoffTime,
1877
+ lastEventId = lastId,
1878
+ bufferCapacity = bufferCapacity,
1879
+ onOpen = onOpen,
1880
+ onClose = onClose,
1881
+ onError = onError,
1882
+ onData = onData,
1883
+ onConnectionError = onConnectionError,
1884
+ )
1885
+ }
1886
+ }`;
1887
+ }
1888
+ function getHeader(options) {
1889
+ return `@file:Suppress(
1890
+ "FunctionName", "LocalVariableName", "UNNECESSARY_NOT_NULL_ASSERTION", "ClassName", "NAME_SHADOWING",
1891
+ "USELESS_IS_CHECK", "unused", "RemoveRedundantQualifierName", "CanBeParameter", "RedundantUnitReturnType",
1892
+ "RedundantExplicitType"
1893
+ )
1894
+
1895
+ import io.ktor.client.*
1896
+ import io.ktor.client.plugins.*
1897
+ import io.ktor.client.request.*
1898
+ import io.ktor.client.statement.*
1899
+ import io.ktor.http.*
1900
+ import io.ktor.utils.io.*
1901
+ import kotlinx.coroutines.CoroutineScope
1902
+ import kotlinx.coroutines.Job
1903
+ import kotlinx.coroutines.launch
1904
+ import kotlinx.coroutines.withContext
1905
+ import kotlinx.serialization.encodeToString
1906
+ import kotlinx.serialization.json.*
1907
+ import java.nio.ByteBuffer
1908
+ import java.time.Instant
1909
+ import java.time.ZoneId
1910
+ import java.time.ZoneOffset
1911
+ import java.time.format.DateTimeFormatter
1912
+
1913
+ private const val generatedClientVersion = "${options.clientVersion}"
1914
+ private val timestampFormatter =
1915
+ DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
1916
+ .withZone(ZoneId.ofOffset("GMT", ZoneOffset.UTC))
1917
+ private val JsonInstance = Json {
1918
+ encodeDefaults = true
1919
+ ignoreUnknownKeys = true
1920
+ }
1921
+ private typealias headersFn = (() -> MutableMap<String, String>?)?`;
1922
+ }
1923
+
1924
+ export { kotlinClientFromAppDefinition, kotlinClientGenerator, kotlinTypeFromSchema };