From 8efb75f9ee841a67aa9ecb9e935b05bb02d023e7 Mon Sep 17 00:00:00 2001 From: Wesley Edwards Date: Sat, 27 Jun 2026 11:40:36 -0600 Subject: [PATCH 1/6] branded types for ids --- .../typed/sdk/TypescriptFetcherSdk.kt | 41 ++++++++++++++----- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/typed/src/main/kotlin/com/lightningkite/lightningserver/typed/sdk/TypescriptFetcherSdk.kt b/typed/src/main/kotlin/com/lightningkite/lightningserver/typed/sdk/TypescriptFetcherSdk.kt index d557a765b..f84f0b46b 100644 --- a/typed/src/main/kotlin/com/lightningkite/lightningserver/typed/sdk/TypescriptFetcherSdk.kt +++ b/typed/src/main/kotlin/com/lightningkite/lightningserver/typed/sdk/TypescriptFetcherSdk.kt @@ -218,18 +218,26 @@ public class TypescriptFetcherSdk( fun String.replaceGenerics(): String = genericMap.entries.fold(this) { acc, (old, new) -> acc.replace(old, new) } - appendLine("export interface ${type.tsType().replaceGenerics()} {") - - val properties = type - .serializableProperties?.map { it.serializer } - ?: type.childSerializersOrNull()?.toList() - ?: emptyList() + if (type.descriptor.isInline) { + val valueType = + type.serializableProperties?.firstOrNull()?.serializer?.tsType()?.replaceGenerics() + val name = type.tsType().replaceGenerics() + appendLine("export type ${name} = Brand<${valueType}, '${name}'>") + } else { + appendLine("export interface ${type.tsType().replaceGenerics()} {") + val properties = type + .serializableProperties?.map { it.serializer } + ?: type.childSerializersOrNull()?.toList() + ?: emptyList() + + for ((idx, prop) in properties.withIndex()) { + appendLine("\t${type.descriptor.getElementName(idx)}: ${prop.tsType().replaceGenerics()}") + } - for ((idx, prop) in properties.withIndex()) { - appendLine("\t${type.descriptor.getElementName(idx)}: ${prop.tsType().replaceGenerics()}") + appendLine('}') } - appendLine('}') + } SerialKind.ENUM -> { @@ -477,7 +485,17 @@ public class TypescriptFetcherSdk( if (descriptor.serialName == "com.lightningkite.serialization.Partial") { append("DeepPartial") } else { - append(descriptor.simpleSerialName) + val name = descriptor.simpleSerialName + if (name == "ID" || name == "Value") { + val parts = descriptor.serialName.split('.') + if (parts.size >= 2) { + val parentName = parts[parts.size - 2] + // If the parent is the containing class, return "Account" + "ID" -> "AccountId" + append("${parentName}${name}") + } + } else { + append(name) + } } typeParametersSerializersOrNull() ?.takeUnless { it.isEmpty() } @@ -517,7 +535,8 @@ public class TypescriptFetcherSdk( "DataClassPathPartial", "QueryPartial", "DeepPartial", - "Fetcher" + "Fetcher", + "Brand", ) private val skipFromLsPackage = setOf("Partial") + fromLightningServerPackage From 12ced97413918fa0dadc53b5be05d091cb382cd0 Mon Sep 17 00:00:00 2001 From: Wesley Edwards Date: Sat, 27 Jun 2026 12:23:35 -0600 Subject: [PATCH 2/6] add namespace for value classes --- .../typed/sdk/TypescriptFetcherSdk.kt | 29 +++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/typed/src/main/kotlin/com/lightningkite/lightningserver/typed/sdk/TypescriptFetcherSdk.kt b/typed/src/main/kotlin/com/lightningkite/lightningserver/typed/sdk/TypescriptFetcherSdk.kt index f84f0b46b..55e55479d 100644 --- a/typed/src/main/kotlin/com/lightningkite/lightningserver/typed/sdk/TypescriptFetcherSdk.kt +++ b/typed/src/main/kotlin/com/lightningkite/lightningserver/typed/sdk/TypescriptFetcherSdk.kt @@ -168,9 +168,7 @@ public class TypescriptFetcherSdk( fun Appendable.appendModelImports() = appendLine( "import type { ${ - models.joinToString { - it.tsType().substringBefore('<') - } + models.map { it.tsTopLevelTypeName() }.distinct().joinToString() } } from './${fileStructure.modelsFilename}'" ) @@ -216,13 +214,22 @@ public class TypescriptFetcherSdk( ?: emptyMap() fun String.replaceGenerics(): String = - genericMap.entries.fold(this) { acc, (old, new) -> acc.replace(old, new) } + genericMap.entries + .sortedByDescending { it.key.length } + .fold(this) { acc, (old, new) -> acc.replace(old, new) } if (type.descriptor.isInline) { val valueType = type.serializableProperties?.firstOrNull()?.serializer?.tsType()?.replaceGenerics() val name = type.tsType().replaceGenerics() - appendLine("export type ${name} = Brand<${valueType}, '${name}'>") + val namespaceParts = name.split('.', limit = 2) + if (namespaceParts.size == 2) { + appendLine("export namespace ${namespaceParts[0]} {") + appendLine("\texport type ${namespaceParts[1]} = Brand<${valueType}, \"$name\">") + appendLine('}') + } else { + appendLine("export type ${name} = Brand<${valueType}, \"$name\">") + } } else { appendLine("export interface ${type.tsType().replaceGenerics()} {") val properties = type @@ -437,6 +444,11 @@ public class TypescriptFetcherSdk( } + context(runtime: ServerRuntime) + private fun KSerializer<*>.tsTopLevelTypeName(): String = tsType() + .substringBefore('<') + .substringBefore('.') + @OptIn(ExperimentalSerializationApi::class) context(runtime: ServerRuntime) private fun KSerializer<*>.tsType(): String = nullElement()?.let { it.tsType() + " | null | undefined" } ?: when { @@ -486,12 +498,11 @@ public class TypescriptFetcherSdk( append("DeepPartial") } else { val name = descriptor.simpleSerialName - if (name == "ID" || name == "Value") { + if (descriptor.isInline && (name == "ID" || name == "Value")) { val parts = descriptor.serialName.split('.') if (parts.size >= 2) { val parentName = parts[parts.size - 2] - // If the parent is the containing class, return "Account" + "ID" -> "AccountId" - append("${parentName}${name}") + append("${parentName}.${name}") } } else { append(name) @@ -540,4 +551,4 @@ public class TypescriptFetcherSdk( ) private val skipFromLsPackage = setOf("Partial") + fromLightningServerPackage -} \ No newline at end of file +} From 4b380a10ec51498bdd554fbccd5ab7121c79422e Mon Sep 17 00:00:00 2001 From: Wesley Edwards Date: Sun, 28 Jun 2026 19:56:01 -0600 Subject: [PATCH 3/6] correctly group namespaces --- .../typed/sdk/TypescriptFetcherSdk.kt | 121 ++++++++++++------ 1 file changed, 80 insertions(+), 41 deletions(-) diff --git a/typed/src/main/kotlin/com/lightningkite/lightningserver/typed/sdk/TypescriptFetcherSdk.kt b/typed/src/main/kotlin/com/lightningkite/lightningserver/typed/sdk/TypescriptFetcherSdk.kt index 55e55479d..2389bd9be 100644 --- a/typed/src/main/kotlin/com/lightningkite/lightningserver/typed/sdk/TypescriptFetcherSdk.kt +++ b/typed/src/main/kotlin/com/lightningkite/lightningserver/typed/sdk/TypescriptFetcherSdk.kt @@ -198,11 +198,29 @@ public class TypescriptFetcherSdk( private fun Appendable.appendLsImports() = appendLine("import type { ${fromLightningServerPackage.joinToString()} } from '@lightningkite/lightning-server-simplified'") + context(server: ServerRuntime) private fun Appendable.writeTypeDefinitions(types: List> = server.models()) { val stringSerialNames = HashSet() + val namespaces = linkedMapOf>() + + fun Appendable.appendNamespaced( + typeName: String, + declaration: Appendable.(localName: String, depth: Int) -> Unit, + ): Boolean { + val namespace = typeName.substringBefore('.', missingDelimiterValue = "") + if (namespace.isBlank()) { + declaration(typeName, 0) + return true + } else { + namespaces.getOrPut(namespace) { ArrayList() } += buildString { + declaration(typeName.substringAfter('.'), 1) + } + return false + } + } - for (type in types) { + fun Appendable.writeType(type: KSerializer<*>): Boolean { when (type.descriptor.kind) { StructureKind.CLASS -> { val genericMap: Map = type @@ -222,55 +240,58 @@ public class TypescriptFetcherSdk( val valueType = type.serializableProperties?.firstOrNull()?.serializer?.tsType()?.replaceGenerics() val name = type.tsType().replaceGenerics() - val namespaceParts = name.split('.', limit = 2) - if (namespaceParts.size == 2) { - appendLine("export namespace ${namespaceParts[0]} {") - appendLine("\texport type ${namespaceParts[1]} = Brand<${valueType}, \"$name\">") - appendLine('}') - } else { - appendLine("export type ${name} = Brand<${valueType}, \"$name\">") + return appendNamespaced(name) { localName, depth -> + appendIdtLine(depth, "export type $localName = Brand<${valueType}, \"$name\">") } } else { - appendLine("export interface ${type.tsType().replaceGenerics()} {") val properties = type .serializableProperties?.map { it.serializer } ?: type.childSerializersOrNull()?.toList() ?: emptyList() - for ((idx, prop) in properties.withIndex()) { - appendLine("\t${type.descriptor.getElementName(idx)}: ${prop.tsType().replaceGenerics()}") - } + return appendNamespaced(type.tsType().replaceGenerics()) { localName, depth -> + appendIdtLine(depth, "export interface $localName {") + for ((idx, prop) in properties.withIndex()) { + appendIdtLine(depth + 1, "${type.descriptor.getElementName(idx)}: ${prop.tsType().replaceGenerics()}") + } - appendLine('}') + appendIdtLine(depth, "}") + } } } SerialKind.ENUM -> { + val typeName = type.tsType() if (erasableTypes) { - append("export type ${type.tsType()} = ") - for (index in 0 until type.descriptor.elementsCount) { - val name = type.descriptor.getElementName(index) - append(if (index == 0) "\"$name\"" else "| \"$name\"") - } - appendLine() - } else { - appendLine("export enum ${type.tsType()} {") - for (index in 0 until type.descriptor.elementsCount) { - append('\t') - val name = type.descriptor.getElementName(index) - name.forEachIndexed { idx, it -> - if ((idx == 0 && it.isJavaIdentifierStart()) || (idx != 0 && it.isJavaIdentifierPart())) - append(it) - else - append('_') + return appendNamespaced(typeName) { localName, depth -> + appendIdt(depth) + append("export type $localName = ") + for (index in 0 until type.descriptor.elementsCount) { + val name = type.descriptor.getElementName(index) + append(if (index == 0) "\"$name\"" else "| \"$name\"") } - append(" = \"$name\",") appendLine() } + } else { + return appendNamespaced(typeName) { localName, depth -> + appendIdtLine(depth, "export enum $localName {") + for (index in 0 until type.descriptor.elementsCount) { + appendIdt(depth + 1) + val name = type.descriptor.getElementName(index) + name.forEachIndexed { idx, it -> + if ((idx == 0 && it.isJavaIdentifierStart()) || (idx != 0 && it.isJavaIdentifierPart())) + append(it) + else + append('_') + } + append(" = \"$name\",") + appendLine() + } - appendLine('}') + appendIdtLine(depth, "}") + } } } @@ -281,15 +302,31 @@ public class TypescriptFetcherSdk( "export type $name = string // ${type.descriptor.serialName}" .replace("/loose", "") ) + return true } + return false } - else -> continue + else -> return false } + } + + for (type in types) { + if (writeType(type)) appendLine() + } + + for ((namespace, declarations) in namespaces) { + appendLine("export namespace $namespace {") + declarations.forEachIndexed { index, declaration -> + if (index > 0) appendLine() + append(declaration) + } + appendLine('}') appendLine() } } + /** * Generates the TypeScript interface definition for the API. * @@ -498,15 +535,7 @@ public class TypescriptFetcherSdk( append("DeepPartial") } else { val name = descriptor.simpleSerialName - if (descriptor.isInline && (name == "ID" || name == "Value")) { - val parts = descriptor.serialName.split('.') - if (parts.size >= 2) { - val parentName = parts[parts.size - 2] - append("${parentName}.${name}") - } - } else { - append(name) - } + append(descriptor.nestedTsTypeName ?: name) } typeParametersSerializersOrNull() ?.takeUnless { it.isEmpty() } @@ -519,6 +548,16 @@ public class TypescriptFetcherSdk( private val SerialDescriptor.simpleSerialName: String get() = serialName.substringBefore('<').substringBefore('/').substringAfterLast('.').removeSuffix("?") + private val SerialDescriptor.nestedTsTypeName: String? + get() { + val parts = serialName.substringBefore('<').substringBefore('/').split('.') + if (parts.size < 2) return null + val name = simpleSerialName + val parentName = parts[parts.size - 2] + if (parentName.firstOrNull()?.isUpperCase() != true) return null + return "$parentName.$name" + } + @OptIn(ExperimentalSerializationApi::class) private fun KSerializer<*>.getGenerics(): Array>? = when (descriptor.kind) { is PolymorphicKind, From 4d55fbed7944f51133328ef011223d82dc99619c Mon Sep 17 00:00:00 2001 From: Wesley Edwards Date: Sun, 28 Jun 2026 20:24:42 -0600 Subject: [PATCH 4/6] ordered namespace grouping --- .../typed/sdk/TypescriptFetcherSdk.kt | 98 +++++++++++++++---- 1 file changed, 77 insertions(+), 21 deletions(-) diff --git a/typed/src/main/kotlin/com/lightningkite/lightningserver/typed/sdk/TypescriptFetcherSdk.kt b/typed/src/main/kotlin/com/lightningkite/lightningserver/typed/sdk/TypescriptFetcherSdk.kt index 2389bd9be..9260684cc 100644 --- a/typed/src/main/kotlin/com/lightningkite/lightningserver/typed/sdk/TypescriptFetcherSdk.kt +++ b/typed/src/main/kotlin/com/lightningkite/lightningserver/typed/sdk/TypescriptFetcherSdk.kt @@ -202,15 +202,21 @@ public class TypescriptFetcherSdk( context(server: ServerRuntime) private fun Appendable.writeTypeDefinitions(types: List> = server.models()) { val stringSerialNames = HashSet() + // TypeScript merges declarations with the same exported name, so nested Kotlin types + // are emitted as namespace members next to their parent interface. We render first, + // collect members like TestModel.ID/Status, then flush each namespace immediately + // after the matching top-level declaration for readability and stable imports. val namespaces = linkedMapOf>() + val topLevelDeclarations = ArrayList>() - fun Appendable.appendNamespaced( + fun String.topLevelDeclarationName(): String = substringBefore('<').substringBefore('.') + + fun Appendable.appendNamespace( typeName: String, declaration: Appendable.(localName: String, depth: Int) -> Unit, ): Boolean { val namespace = typeName.substringBefore('.', missingDelimiterValue = "") if (namespace.isBlank()) { - declaration(typeName, 0) return true } else { namespaces.getOrPut(namespace) { ArrayList() } += buildString { @@ -220,7 +226,18 @@ public class TypescriptFetcherSdk( } } - fun Appendable.writeType(type: KSerializer<*>): Boolean { + fun Appendable.appendNamespace(namespace: String) { + val declarations = namespaces.remove(namespace) ?: return + appendLine("export namespace $namespace {") + declarations.forEachIndexed { index, declaration -> + if (index > 0) appendLine() + append(declaration) + } + appendLine('}') + appendLine() + } + + fun renderType(type: KSerializer<*>): Pair? { when (type.descriptor.kind) { StructureKind.CLASS -> { val genericMap: Map = type @@ -240,8 +257,11 @@ public class TypescriptFetcherSdk( val valueType = type.serializableProperties?.firstOrNull()?.serializer?.tsType()?.replaceGenerics() val name = type.tsType().replaceGenerics() - return appendNamespaced(name) { localName, depth -> + if (!StringBuilder().appendNamespace(name) { localName, depth -> appendIdtLine(depth, "export type $localName = Brand<${valueType}, \"$name\">") + }) return null + return name.topLevelDeclarationName() to buildString { + appendLine("export type $name = Brand<${valueType}, \"$name\">") } } else { val properties = type @@ -249,13 +269,22 @@ public class TypescriptFetcherSdk( ?: type.childSerializersOrNull()?.toList() ?: emptyList() - return appendNamespaced(type.tsType().replaceGenerics()) { localName, depth -> + val name = type.tsType().replaceGenerics() + if (!StringBuilder().appendNamespace(name) { localName, depth -> appendIdtLine(depth, "export interface $localName {") for ((idx, prop) in properties.withIndex()) { appendIdtLine(depth + 1, "${type.descriptor.getElementName(idx)}: ${prop.tsType().replaceGenerics()}") } appendIdtLine(depth, "}") + }) return null + return name.topLevelDeclarationName() to buildString { + appendLine("export interface $name {") + for ((idx, prop) in properties.withIndex()) { + appendIdtLine(1, "${type.descriptor.getElementName(idx)}: ${prop.tsType().replaceGenerics()}") + } + + appendLine("}") } } @@ -265,7 +294,7 @@ public class TypescriptFetcherSdk( SerialKind.ENUM -> { val typeName = type.tsType() if (erasableTypes) { - return appendNamespaced(typeName) { localName, depth -> + if (!StringBuilder().appendNamespace(typeName) { localName, depth -> appendIdt(depth) append("export type $localName = ") for (index in 0 until type.descriptor.elementsCount) { @@ -273,9 +302,17 @@ public class TypescriptFetcherSdk( append(if (index == 0) "\"$name\"" else "| \"$name\"") } appendLine() + }) return null + return typeName.topLevelDeclarationName() to buildString { + append("export type $typeName = ") + for (index in 0 until type.descriptor.elementsCount) { + val name = type.descriptor.getElementName(index) + append(if (index == 0) "\"$name\"" else "| \"$name\"") + } + appendLine() } } else { - return appendNamespaced(typeName) { localName, depth -> + if (!StringBuilder().appendNamespace(typeName) { localName, depth -> appendIdtLine(depth, "export enum $localName {") for (index in 0 until type.descriptor.elementsCount) { appendIdt(depth + 1) @@ -291,6 +328,23 @@ public class TypescriptFetcherSdk( } appendIdtLine(depth, "}") + }) return null + return typeName.topLevelDeclarationName() to buildString { + appendLine("export enum $typeName {") + for (index in 0 until type.descriptor.elementsCount) { + appendIdt(1) + val name = type.descriptor.getElementName(index) + name.forEachIndexed { idx, it -> + if ((idx == 0 && it.isJavaIdentifierStart()) || (idx != 0 && it.isJavaIdentifierPart())) + append(it) + else + append('_') + } + append(" = \"$name\",") + appendLine() + } + + appendLine("}") } } } @@ -298,32 +352,34 @@ public class TypescriptFetcherSdk( PrimitiveKind.STRING -> { val name = type.descriptor.simpleSerialName if (name != "String" && stringSerialNames.add(name)) { - appendLine( - "export type $name = string // ${type.descriptor.serialName}" + return name to buildString { + appendLine( + "export type $name = string // ${type.descriptor.serialName}" .replace("/loose", "") - ) - return true + ) + } } - return false + return null } - else -> return false + else -> return null } } for (type in types) { - if (writeType(type)) appendLine() + renderType(type)?.let { topLevelDeclarations += it.also { + println("1 ------- : ${it.first}") + println("2 ------- :: ${it.second}") + } } } - for ((namespace, declarations) in namespaces) { - appendLine("export namespace $namespace {") - declarations.forEachIndexed { index, declaration -> - if (index > 0) appendLine() - append(declaration) - } - appendLine('}') + for ((name, declaration) in topLevelDeclarations) { + append(declaration) appendLine() + appendNamespace(name) } + + namespaces.keys.toList().forEach { appendNamespace(it) } } From d9ab48565d146a1a7f3751b5d10166413436eaeb Mon Sep 17 00:00:00 2001 From: Wesley Edwards Date: Sun, 28 Jun 2026 20:33:27 -0600 Subject: [PATCH 5/6] regenerate sdks --- .../typed/sdk/TypescriptFetcherSdk.kt | 5 +- .../typed/sdk/generated/kotlin/Api.kt | 12 +- .../typed/sdk/generated/kotlin/LiveApi.kt | 12 +- .../typed/sdk/generated/kotlin/sdk.kt | 24 ++-- .../typed/sdk/generated/typescript/Api.ts | 88 ++++++------ .../typed/sdk/generated/typescript/LiveApi.ts | 4 +- .../typed/sdk/generated/typescript/models.ts | 48 +++++-- .../typed/sdk/generated/typescript/sdk.ts | 132 ++++++++++-------- .../lightningserver/typed/sdk/server.kt | 22 ++- 9 files changed, 199 insertions(+), 148 deletions(-) diff --git a/typed/src/main/kotlin/com/lightningkite/lightningserver/typed/sdk/TypescriptFetcherSdk.kt b/typed/src/main/kotlin/com/lightningkite/lightningserver/typed/sdk/TypescriptFetcherSdk.kt index 9260684cc..61dd108a4 100644 --- a/typed/src/main/kotlin/com/lightningkite/lightningserver/typed/sdk/TypescriptFetcherSdk.kt +++ b/typed/src/main/kotlin/com/lightningkite/lightningserver/typed/sdk/TypescriptFetcherSdk.kt @@ -367,10 +367,7 @@ public class TypescriptFetcherSdk( } for (type in types) { - renderType(type)?.let { topLevelDeclarations += it.also { - println("1 ------- : ${it.first}") - println("2 ------- :: ${it.second}") - } } + renderType(type)?.let { topLevelDeclarations += it } } for ((name, declaration) in topLevelDeclarations) { diff --git a/typed/src/test/kotlin/com/lightningkite/lightningserver/typed/sdk/generated/kotlin/Api.kt b/typed/src/test/kotlin/com/lightningkite/lightningserver/typed/sdk/generated/kotlin/Api.kt index fd0be087d..0ce5f5d4b 100644 --- a/typed/src/test/kotlin/com/lightningkite/lightningserver/typed/sdk/generated/kotlin/Api.kt +++ b/typed/src/test/kotlin/com/lightningkite/lightningserver/typed/sdk/generated/kotlin/Api.kt @@ -39,7 +39,7 @@ interface Api { } val predefinedEndpoints: PredefinedEndpoints - interface ModuleApi : com.lightningkite.lightningserver.typed.ClientModelRestEndpoints { + interface ModuleApi : com.lightningkite.lightningserver.typed.ClientModelRestEndpoints { /** * Test Endpoint * @@ -65,7 +65,7 @@ interface Api { * */ suspend fun inlinedEndpoint2(id: kotlin.uuid.Uuid, category: kotlin.uuid.Uuid): kotlin.Int - interface DefaultEndpoints : com.lightningkite.lightningserver.typed.ClientModelRestEndpointsAndUpdatesWebsocket { + interface DefaultEndpoints : com.lightningkite.lightningserver.typed.ClientModelRestEndpointsAndUpdatesWebsocket { /** * Test Endpoint * @@ -89,7 +89,7 @@ interface Api { } val default: DefaultEndpoints - interface DefaultEndpoints2 : com.lightningkite.lightningserver.typed.ClientModelRestEndpointsAndUpdatesWebsocket { + interface DefaultEndpoints2 : com.lightningkite.lightningserver.typed.ClientModelRestEndpointsAndUpdatesWebsocket { /** * Test Endpoint * @@ -115,7 +115,7 @@ interface Api { } val module: ModuleApi - interface CustomEndpoints : com.lightningkite.lightningserver.typed.ClientModelRestEndpointsAndUpdatesWebsocket { + interface CustomEndpoints : com.lightningkite.lightningserver.typed.ClientModelRestEndpointsAndUpdatesWebsocket { /** * Test Endpoint * @@ -157,9 +157,9 @@ interface Api { * */ suspend fun inlinedEndpoint(id: kotlin.uuid.Uuid, category: kotlin.uuid.Uuid): kotlin.Int - val rest: com.lightningkite.lightningserver.typed.ClientModelRestEndpoints + val rest: com.lightningkite.lightningserver.typed.ClientModelRestEndpoints - val rest2: com.lightningkite.lightningserver.typed.ClientModelRestEndpoints + val rest2: com.lightningkite.lightningserver.typed.ClientModelRestEndpoints } val other: OtherEndpoints } diff --git a/typed/src/test/kotlin/com/lightningkite/lightningserver/typed/sdk/generated/kotlin/LiveApi.kt b/typed/src/test/kotlin/com/lightningkite/lightningserver/typed/sdk/generated/kotlin/LiveApi.kt index 56a4a10f3..5b5a9cf6b 100644 --- a/typed/src/test/kotlin/com/lightningkite/lightningserver/typed/sdk/generated/kotlin/LiveApi.kt +++ b/typed/src/test/kotlin/com/lightningkite/lightningserver/typed/sdk/generated/kotlin/LiveApi.kt @@ -24,7 +24,7 @@ class LiveApi(val fetcher: Fetcher) : Api { } override val predefinedEndpoints = LivePredefinedEndpoints() - inner class LiveModuleApi : Api.ModuleApi, com.lightningkite.lightningserver.typed.ClientModelRestEndpoints by com.lightningkite.lightningserver.typed.LiveClientModelRestEndpoints(fetcher, "m1/rest", com.lightningkite.lightningserver.typed.sdk.TestModel.serializer(), kotlin.uuid.Uuid.serializer()) { + inner class LiveModuleApi : Api.ModuleApi, com.lightningkite.lightningserver.typed.ClientModelRestEndpoints by com.lightningkite.lightningserver.typed.LiveClientModelRestEndpoints(fetcher, "m1/rest", com.lightningkite.lightningserver.typed.sdk.TestModel.serializer(), com.lightningkite.lightningserver.typed.sdk.TestModel.ID.serializer()) { override suspend fun testSdkEndpoint(first: kotlin.String, input: com.lightningkite.lightningserver.typed.sdk.TestInput): kotlin.String = fetcher("m1/endpoint/${fetcher.url(first, kotlin.String.serializer())}", HttpMethod.POST, com.lightningkite.lightningserver.typed.sdk.TestInput.serializer(), input, kotlin.String.serializer()) override suspend fun inlinedEndpoint(id: kotlin.uuid.Uuid, category: kotlin.uuid.Uuid): kotlin.Int = @@ -32,7 +32,7 @@ class LiveApi(val fetcher: Fetcher) : Api { override suspend fun inlinedEndpoint2(id: kotlin.uuid.Uuid, category: kotlin.uuid.Uuid): kotlin.Int = fetcher("m1/inline/action/${fetcher.url(id, kotlin.uuid.Uuid.serializer())}/${fetcher.url(category, kotlin.uuid.Uuid.serializer())}", HttpMethod.POST, kotlin.Unit.serializer(), kotlin.Unit, kotlin.Int.serializer()) - inner class LiveDefaultEndpoints : Api.ModuleApi.DefaultEndpoints, com.lightningkite.lightningserver.typed.ClientModelRestEndpointsAndUpdatesWebsocket by com.lightningkite.lightningserver.typed.LiveClientModelRestEndpointsAndUpdatesWebsocket(fetcher, "m1/second/rest", com.lightningkite.lightningserver.typed.sdk.TestModel.serializer(), kotlin.uuid.Uuid.serializer()) { + inner class LiveDefaultEndpoints : Api.ModuleApi.DefaultEndpoints, com.lightningkite.lightningserver.typed.ClientModelRestEndpointsAndUpdatesWebsocket by com.lightningkite.lightningserver.typed.LiveClientModelRestEndpointsAndUpdatesWebsocket(fetcher, "m1/second/rest", com.lightningkite.lightningserver.typed.sdk.TestModel.serializer(), com.lightningkite.lightningserver.typed.sdk.TestModel.ID.serializer()) { override suspend fun testSdkEndpoint(second: kotlin.String, input: com.lightningkite.lightningserver.typed.sdk.TestInput): kotlin.String = fetcher("m1/second/endpoint/${fetcher.url(second, kotlin.String.serializer())}", HttpMethod.POST, com.lightningkite.lightningserver.typed.sdk.TestInput.serializer(), input, kotlin.String.serializer()) @@ -44,7 +44,7 @@ class LiveApi(val fetcher: Fetcher) : Api { } override val default = LiveDefaultEndpoints() - inner class LiveDefaultEndpoints2 : Api.ModuleApi.DefaultEndpoints2, com.lightningkite.lightningserver.typed.ClientModelRestEndpointsAndUpdatesWebsocket by com.lightningkite.lightningserver.typed.LiveClientModelRestEndpointsAndUpdatesWebsocket(fetcher, "m1/duplicate/rest", com.lightningkite.lightningserver.typed.sdk.TestModel.serializer(), kotlin.uuid.Uuid.serializer()) { + inner class LiveDefaultEndpoints2 : Api.ModuleApi.DefaultEndpoints2, com.lightningkite.lightningserver.typed.ClientModelRestEndpointsAndUpdatesWebsocket by com.lightningkite.lightningserver.typed.LiveClientModelRestEndpointsAndUpdatesWebsocket(fetcher, "m1/duplicate/rest", com.lightningkite.lightningserver.typed.sdk.TestModel.serializer(), com.lightningkite.lightningserver.typed.sdk.TestModel.ID.serializer()) { override suspend fun testSdkEndpoint(second: kotlin.String, input: com.lightningkite.lightningserver.typed.sdk.TestInput): kotlin.String = fetcher("m1/duplicate/endpoint/${fetcher.url(second, kotlin.String.serializer())}", HttpMethod.POST, com.lightningkite.lightningserver.typed.sdk.TestInput.serializer(), input, kotlin.String.serializer()) @@ -58,7 +58,7 @@ class LiveApi(val fetcher: Fetcher) : Api { } override val module = LiveModuleApi() - inner class LiveCustomEndpoints : Api.CustomEndpoints, com.lightningkite.lightningserver.typed.ClientModelRestEndpointsAndUpdatesWebsocket by com.lightningkite.lightningserver.typed.LiveClientModelRestEndpointsAndUpdatesWebsocket(fetcher, "m2/rest", com.lightningkite.lightningserver.typed.sdk.TestModel.serializer(), kotlin.uuid.Uuid.serializer()) { + inner class LiveCustomEndpoints : Api.CustomEndpoints, com.lightningkite.lightningserver.typed.ClientModelRestEndpointsAndUpdatesWebsocket by com.lightningkite.lightningserver.typed.LiveClientModelRestEndpointsAndUpdatesWebsocket(fetcher, "m2/rest", com.lightningkite.lightningserver.typed.sdk.TestModel.serializer(), com.lightningkite.lightningserver.typed.sdk.TestModel.ID.serializer()) { override suspend fun testSdkEndpoint(second: kotlin.String, input: com.lightningkite.lightningserver.typed.sdk.TestInput): kotlin.String = fetcher("m2/endpoint/${fetcher.url(second, kotlin.String.serializer())}", HttpMethod.POST, com.lightningkite.lightningserver.typed.sdk.TestInput.serializer(), input, kotlin.String.serializer()) @@ -76,9 +76,9 @@ class LiveApi(val fetcher: Fetcher) : Api { override suspend fun inlinedEndpoint(id: kotlin.uuid.Uuid, category: kotlin.uuid.Uuid): kotlin.Int = fetcher("third/inline/action/${fetcher.url(id, kotlin.uuid.Uuid.serializer())}/${fetcher.url(category, kotlin.uuid.Uuid.serializer())}", HttpMethod.POST, kotlin.Unit.serializer(), kotlin.Unit, kotlin.Int.serializer()) - override val rest = com.lightningkite.lightningserver.typed.LiveClientModelRestEndpoints(fetcher, "third/rest", com.lightningkite.lightningserver.typed.sdk.TestModel.serializer(), kotlin.uuid.Uuid.serializer()) + override val rest = com.lightningkite.lightningserver.typed.LiveClientModelRestEndpoints(fetcher, "third/rest", com.lightningkite.lightningserver.typed.sdk.TestModel.serializer(), com.lightningkite.lightningserver.typed.sdk.TestModel.ID.serializer()) - override val rest2 = com.lightningkite.lightningserver.typed.LiveClientModelRestEndpoints(fetcher, "third/rest2", com.lightningkite.lightningserver.typed.sdk.TestModel.serializer(), kotlin.uuid.Uuid.serializer()) + override val rest2 = com.lightningkite.lightningserver.typed.LiveClientModelRestEndpoints(fetcher, "third/rest2", com.lightningkite.lightningserver.typed.sdk.TestModel.serializer(), com.lightningkite.lightningserver.typed.sdk.TestModel.ID.serializer()) } override val other = LiveOtherEndpoints() } diff --git a/typed/src/test/kotlin/com/lightningkite/lightningserver/typed/sdk/generated/kotlin/sdk.kt b/typed/src/test/kotlin/com/lightningkite/lightningserver/typed/sdk/generated/kotlin/sdk.kt index 1bec3b3f0..0aebd9cec 100644 --- a/typed/src/test/kotlin/com/lightningkite/lightningserver/typed/sdk/generated/kotlin/sdk.kt +++ b/typed/src/test/kotlin/com/lightningkite/lightningserver/typed/sdk/generated/kotlin/sdk.kt @@ -45,7 +45,7 @@ interface Api { } val predefinedEndpoints: PredefinedEndpoints - interface ModuleApi : com.lightningkite.lightningserver.typed.ClientModelRestEndpoints { + interface ModuleApi : com.lightningkite.lightningserver.typed.ClientModelRestEndpoints { /** * Test Endpoint * @@ -71,7 +71,7 @@ interface Api { * */ suspend fun inlinedEndpoint2(id: kotlin.uuid.Uuid, category: kotlin.uuid.Uuid): kotlin.Int - interface DefaultEndpoints : com.lightningkite.lightningserver.typed.ClientModelRestEndpointsAndUpdatesWebsocket { + interface DefaultEndpoints : com.lightningkite.lightningserver.typed.ClientModelRestEndpointsAndUpdatesWebsocket { /** * Test Endpoint * @@ -95,7 +95,7 @@ interface Api { } val default: DefaultEndpoints - interface DefaultEndpoints2 : com.lightningkite.lightningserver.typed.ClientModelRestEndpointsAndUpdatesWebsocket { + interface DefaultEndpoints2 : com.lightningkite.lightningserver.typed.ClientModelRestEndpointsAndUpdatesWebsocket { /** * Test Endpoint * @@ -121,7 +121,7 @@ interface Api { } val module: ModuleApi - interface CustomEndpoints : com.lightningkite.lightningserver.typed.ClientModelRestEndpointsAndUpdatesWebsocket { + interface CustomEndpoints : com.lightningkite.lightningserver.typed.ClientModelRestEndpointsAndUpdatesWebsocket { /** * Test Endpoint * @@ -163,9 +163,9 @@ interface Api { * */ suspend fun inlinedEndpoint(id: kotlin.uuid.Uuid, category: kotlin.uuid.Uuid): kotlin.Int - val rest: com.lightningkite.lightningserver.typed.ClientModelRestEndpoints + val rest: com.lightningkite.lightningserver.typed.ClientModelRestEndpoints - val rest2: com.lightningkite.lightningserver.typed.ClientModelRestEndpoints + val rest2: com.lightningkite.lightningserver.typed.ClientModelRestEndpoints } val other: OtherEndpoints } @@ -186,7 +186,7 @@ class LiveApi(val fetcher: Fetcher) : Api { } override val predefinedEndpoints = LivePredefinedEndpoints() - inner class LiveModuleApi : Api.ModuleApi, com.lightningkite.lightningserver.typed.ClientModelRestEndpoints by com.lightningkite.lightningserver.typed.LiveClientModelRestEndpoints(fetcher, "m1/rest", com.lightningkite.lightningserver.typed.sdk.TestModel.serializer(), kotlin.uuid.Uuid.serializer()) { + inner class LiveModuleApi : Api.ModuleApi, com.lightningkite.lightningserver.typed.ClientModelRestEndpoints by com.lightningkite.lightningserver.typed.LiveClientModelRestEndpoints(fetcher, "m1/rest", com.lightningkite.lightningserver.typed.sdk.TestModel.serializer(), com.lightningkite.lightningserver.typed.sdk.TestModel.ID.serializer()) { override suspend fun testSdkEndpoint(first: kotlin.String, input: com.lightningkite.lightningserver.typed.sdk.TestInput): kotlin.String = fetcher("m1/endpoint/${fetcher.url(first, kotlin.String.serializer())}", HttpMethod.POST, com.lightningkite.lightningserver.typed.sdk.TestInput.serializer(), input, kotlin.String.serializer()) override suspend fun inlinedEndpoint(id: kotlin.uuid.Uuid, category: kotlin.uuid.Uuid): kotlin.Int = @@ -194,7 +194,7 @@ class LiveApi(val fetcher: Fetcher) : Api { override suspend fun inlinedEndpoint2(id: kotlin.uuid.Uuid, category: kotlin.uuid.Uuid): kotlin.Int = fetcher("m1/inline/action/${fetcher.url(id, kotlin.uuid.Uuid.serializer())}/${fetcher.url(category, kotlin.uuid.Uuid.serializer())}", HttpMethod.POST, kotlin.Unit.serializer(), kotlin.Unit, kotlin.Int.serializer()) - inner class LiveDefaultEndpoints : Api.ModuleApi.DefaultEndpoints, com.lightningkite.lightningserver.typed.ClientModelRestEndpointsAndUpdatesWebsocket by com.lightningkite.lightningserver.typed.LiveClientModelRestEndpointsAndUpdatesWebsocket(fetcher, "m1/second/rest", com.lightningkite.lightningserver.typed.sdk.TestModel.serializer(), kotlin.uuid.Uuid.serializer()) { + inner class LiveDefaultEndpoints : Api.ModuleApi.DefaultEndpoints, com.lightningkite.lightningserver.typed.ClientModelRestEndpointsAndUpdatesWebsocket by com.lightningkite.lightningserver.typed.LiveClientModelRestEndpointsAndUpdatesWebsocket(fetcher, "m1/second/rest", com.lightningkite.lightningserver.typed.sdk.TestModel.serializer(), com.lightningkite.lightningserver.typed.sdk.TestModel.ID.serializer()) { override suspend fun testSdkEndpoint(second: kotlin.String, input: com.lightningkite.lightningserver.typed.sdk.TestInput): kotlin.String = fetcher("m1/second/endpoint/${fetcher.url(second, kotlin.String.serializer())}", HttpMethod.POST, com.lightningkite.lightningserver.typed.sdk.TestInput.serializer(), input, kotlin.String.serializer()) @@ -206,7 +206,7 @@ class LiveApi(val fetcher: Fetcher) : Api { } override val default = LiveDefaultEndpoints() - inner class LiveDefaultEndpoints2 : Api.ModuleApi.DefaultEndpoints2, com.lightningkite.lightningserver.typed.ClientModelRestEndpointsAndUpdatesWebsocket by com.lightningkite.lightningserver.typed.LiveClientModelRestEndpointsAndUpdatesWebsocket(fetcher, "m1/duplicate/rest", com.lightningkite.lightningserver.typed.sdk.TestModel.serializer(), kotlin.uuid.Uuid.serializer()) { + inner class LiveDefaultEndpoints2 : Api.ModuleApi.DefaultEndpoints2, com.lightningkite.lightningserver.typed.ClientModelRestEndpointsAndUpdatesWebsocket by com.lightningkite.lightningserver.typed.LiveClientModelRestEndpointsAndUpdatesWebsocket(fetcher, "m1/duplicate/rest", com.lightningkite.lightningserver.typed.sdk.TestModel.serializer(), com.lightningkite.lightningserver.typed.sdk.TestModel.ID.serializer()) { override suspend fun testSdkEndpoint(second: kotlin.String, input: com.lightningkite.lightningserver.typed.sdk.TestInput): kotlin.String = fetcher("m1/duplicate/endpoint/${fetcher.url(second, kotlin.String.serializer())}", HttpMethod.POST, com.lightningkite.lightningserver.typed.sdk.TestInput.serializer(), input, kotlin.String.serializer()) @@ -220,7 +220,7 @@ class LiveApi(val fetcher: Fetcher) : Api { } override val module = LiveModuleApi() - inner class LiveCustomEndpoints : Api.CustomEndpoints, com.lightningkite.lightningserver.typed.ClientModelRestEndpointsAndUpdatesWebsocket by com.lightningkite.lightningserver.typed.LiveClientModelRestEndpointsAndUpdatesWebsocket(fetcher, "m2/rest", com.lightningkite.lightningserver.typed.sdk.TestModel.serializer(), kotlin.uuid.Uuid.serializer()) { + inner class LiveCustomEndpoints : Api.CustomEndpoints, com.lightningkite.lightningserver.typed.ClientModelRestEndpointsAndUpdatesWebsocket by com.lightningkite.lightningserver.typed.LiveClientModelRestEndpointsAndUpdatesWebsocket(fetcher, "m2/rest", com.lightningkite.lightningserver.typed.sdk.TestModel.serializer(), com.lightningkite.lightningserver.typed.sdk.TestModel.ID.serializer()) { override suspend fun testSdkEndpoint(second: kotlin.String, input: com.lightningkite.lightningserver.typed.sdk.TestInput): kotlin.String = fetcher("m2/endpoint/${fetcher.url(second, kotlin.String.serializer())}", HttpMethod.POST, com.lightningkite.lightningserver.typed.sdk.TestInput.serializer(), input, kotlin.String.serializer()) @@ -238,9 +238,9 @@ class LiveApi(val fetcher: Fetcher) : Api { override suspend fun inlinedEndpoint(id: kotlin.uuid.Uuid, category: kotlin.uuid.Uuid): kotlin.Int = fetcher("third/inline/action/${fetcher.url(id, kotlin.uuid.Uuid.serializer())}/${fetcher.url(category, kotlin.uuid.Uuid.serializer())}", HttpMethod.POST, kotlin.Unit.serializer(), kotlin.Unit, kotlin.Int.serializer()) - override val rest = com.lightningkite.lightningserver.typed.LiveClientModelRestEndpoints(fetcher, "third/rest", com.lightningkite.lightningserver.typed.sdk.TestModel.serializer(), kotlin.uuid.Uuid.serializer()) + override val rest = com.lightningkite.lightningserver.typed.LiveClientModelRestEndpoints(fetcher, "third/rest", com.lightningkite.lightningserver.typed.sdk.TestModel.serializer(), com.lightningkite.lightningserver.typed.sdk.TestModel.ID.serializer()) - override val rest2 = com.lightningkite.lightningserver.typed.LiveClientModelRestEndpoints(fetcher, "third/rest2", com.lightningkite.lightningserver.typed.sdk.TestModel.serializer(), kotlin.uuid.Uuid.serializer()) + override val rest2 = com.lightningkite.lightningserver.typed.LiveClientModelRestEndpoints(fetcher, "third/rest2", com.lightningkite.lightningserver.typed.sdk.TestModel.serializer(), com.lightningkite.lightningserver.typed.sdk.TestModel.ID.serializer()) } override val other = LiveOtherEndpoints() } diff --git a/typed/src/test/kotlin/com/lightningkite/lightningserver/typed/sdk/generated/typescript/Api.ts b/typed/src/test/kotlin/com/lightningkite/lightningserver/typed/sdk/generated/typescript/Api.ts index ebaf46e09..ddb288fa2 100644 --- a/typed/src/test/kotlin/com/lightningkite/lightningserver/typed/sdk/generated/typescript/Api.ts +++ b/typed/src/test/kotlin/com/lightningkite/lightningserver/typed/sdk/generated/typescript/Api.ts @@ -1,5 +1,5 @@ -import type { Query, MassModification, EntryChange, ListChange, Modification, Condition, GroupCountQuery, AggregateQuery, GroupAggregateQuery, Aggregate, SortPart, DataClassPath, DataClassPathPartial, QueryPartial, DeepPartial, Fetcher } from '@lightningkite/lightning-server-simplified' -import type { CollectionUpdates, Mask, Mode, ModelPermissions, Pair, Part, TestInput, TestModel, UpdateRestrictions, Uuid } from './models.ts' +import type { Query, MassModification, EntryChange, ListChange, Modification, Condition, GroupCountQuery, AggregateQuery, GroupAggregateQuery, Aggregate, SortPart, DataClassPath, DataClassPathPartial, QueryPartial, DeepPartial, Fetcher, Brand } from '@lightningkite/lightning-server-simplified' +import type { CollectionUpdates, TestModel, Mask, UpdateRestrictions, ModelPermissions, Pair, TestInput, Uuid } from './models.ts' export interface Api { index(): Promise @@ -28,13 +28,13 @@ export interface Api { groupCount2(input: GroupCountQuery): Promise> bulkDelete(input: Condition): Promise aggregate(input: AggregateQuery): Promise - detail(id: Uuid): Promise - upsert(id: Uuid, input: TestModel): Promise - replace(id: Uuid, input: TestModel): Promise - modify(id: Uuid, input: Modification): Promise - delete(id: Uuid): Promise - simplifiedModify(id: Uuid, input: Partial): Promise - modifyWithDiff(id: Uuid, input: Modification): Promise> + detail(id: TestModel.ID): Promise + upsert(id: TestModel.ID, input: TestModel): Promise + replace(id: TestModel.ID, input: TestModel): Promise + modify(id: TestModel.ID, input: Modification): Promise + delete(id: TestModel.ID): Promise + simplifiedModify(id: TestModel.ID, input: Partial): Promise + modifyWithDiff(id: TestModel.ID, input: Modification): Promise> readonly default: { testSdkEndpoint(second: string, input: TestInput): Promise @@ -53,13 +53,13 @@ export interface Api { groupCount2(input: GroupCountQuery): Promise> bulkDelete(input: Condition): Promise aggregate(input: AggregateQuery): Promise - detail(id: Uuid): Promise - upsert(id: Uuid, input: TestModel): Promise - replace(id: Uuid, input: TestModel): Promise - modify(id: Uuid, input: Modification): Promise - delete(id: Uuid): Promise - simplifiedModify(id: Uuid, input: Partial): Promise - modifyWithDiff(id: Uuid, input: Modification): Promise> + detail(id: TestModel.ID): Promise + upsert(id: TestModel.ID, input: TestModel): Promise + replace(id: TestModel.ID, input: TestModel): Promise + modify(id: TestModel.ID, input: Modification): Promise + delete(id: TestModel.ID): Promise + simplifiedModify(id: TestModel.ID, input: Partial): Promise + modifyWithDiff(id: TestModel.ID, input: Modification): Promise> readonly notInlined: { inlinedEndpoint(id: Uuid, category: Uuid): Promise @@ -82,13 +82,13 @@ export interface Api { groupCount2(input: GroupCountQuery): Promise> bulkDelete(input: Condition): Promise aggregate(input: AggregateQuery): Promise - detail(id: Uuid): Promise - upsert(id: Uuid, input: TestModel): Promise - replace(id: Uuid, input: TestModel): Promise - modify(id: Uuid, input: Modification): Promise - delete(id: Uuid): Promise - simplifiedModify(id: Uuid, input: Partial): Promise - modifyWithDiff(id: Uuid, input: Modification): Promise> + detail(id: TestModel.ID): Promise + upsert(id: TestModel.ID, input: TestModel): Promise + replace(id: TestModel.ID, input: TestModel): Promise + modify(id: TestModel.ID, input: Modification): Promise + delete(id: TestModel.ID): Promise + simplifiedModify(id: TestModel.ID, input: Partial): Promise + modifyWithDiff(id: TestModel.ID, input: Modification): Promise> readonly notInlined: { inlinedEndpoint(id: Uuid, category: Uuid): Promise @@ -112,13 +112,13 @@ export interface Api { groupCount2(input: GroupCountQuery): Promise> bulkDelete(input: Condition): Promise aggregate(input: AggregateQuery): Promise - detail(id: Uuid): Promise - upsert(id: Uuid, input: TestModel): Promise - replace(id: Uuid, input: TestModel): Promise - modify(id: Uuid, input: Modification): Promise - delete(id: Uuid): Promise - simplifiedModify(id: Uuid, input: Partial): Promise - modifyWithDiff(id: Uuid, input: Modification): Promise> + detail(id: TestModel.ID): Promise + upsert(id: TestModel.ID, input: TestModel): Promise + replace(id: TestModel.ID, input: TestModel): Promise + modify(id: TestModel.ID, input: Modification): Promise + delete(id: TestModel.ID): Promise + simplifiedModify(id: TestModel.ID, input: Partial): Promise + modifyWithDiff(id: TestModel.ID, input: Modification): Promise> readonly notInlined: { inlinedEndpoint(id: Uuid, category: Uuid): Promise @@ -144,13 +144,13 @@ export interface Api { groupCount2(input: GroupCountQuery): Promise> bulkDelete(input: Condition): Promise aggregate(input: AggregateQuery): Promise - detail(id: Uuid): Promise - upsert(id: Uuid, input: TestModel): Promise - replace(id: Uuid, input: TestModel): Promise - modify(id: Uuid, input: Modification): Promise - delete(id: Uuid): Promise - simplifiedModify(id: Uuid, input: Partial): Promise - modifyWithDiff(id: Uuid, input: Modification): Promise> + detail(id: TestModel.ID): Promise + upsert(id: TestModel.ID, input: TestModel): Promise + replace(id: TestModel.ID, input: TestModel): Promise + modify(id: TestModel.ID, input: Modification): Promise + delete(id: TestModel.ID): Promise + simplifiedModify(id: TestModel.ID, input: Partial): Promise + modifyWithDiff(id: TestModel.ID, input: Modification): Promise> } readonly rest2: { list(input: Query): Promise> @@ -168,13 +168,13 @@ export interface Api { groupCount2(input: GroupCountQuery): Promise> bulkDelete(input: Condition): Promise aggregate(input: AggregateQuery): Promise - detail(id: Uuid): Promise - upsert(id: Uuid, input: TestModel): Promise - replace(id: Uuid, input: TestModel): Promise - modify(id: Uuid, input: Modification): Promise - delete(id: Uuid): Promise - simplifiedModify(id: Uuid, input: Partial): Promise - modifyWithDiff(id: Uuid, input: Modification): Promise> + detail(id: TestModel.ID): Promise + upsert(id: TestModel.ID, input: TestModel): Promise + replace(id: TestModel.ID, input: TestModel): Promise + modify(id: TestModel.ID, input: Modification): Promise + delete(id: TestModel.ID): Promise + simplifiedModify(id: TestModel.ID, input: Partial): Promise + modifyWithDiff(id: TestModel.ID, input: Modification): Promise> } } } diff --git a/typed/src/test/kotlin/com/lightningkite/lightningserver/typed/sdk/generated/typescript/LiveApi.ts b/typed/src/test/kotlin/com/lightningkite/lightningserver/typed/sdk/generated/typescript/LiveApi.ts index b96106ea6..04e940933 100644 --- a/typed/src/test/kotlin/com/lightningkite/lightningserver/typed/sdk/generated/typescript/LiveApi.ts +++ b/typed/src/test/kotlin/com/lightningkite/lightningserver/typed/sdk/generated/typescript/LiveApi.ts @@ -1,5 +1,5 @@ -import type { Query, MassModification, EntryChange, ListChange, Modification, Condition, GroupCountQuery, AggregateQuery, GroupAggregateQuery, Aggregate, SortPart, DataClassPath, DataClassPathPartial, QueryPartial, DeepPartial, Fetcher } from '@lightningkite/lightning-server-simplified' -import type { CollectionUpdates, Mask, Mode, ModelPermissions, Pair, Part, TestInput, TestModel, UpdateRestrictions, Uuid } from './models.ts' +import type { Query, MassModification, EntryChange, ListChange, Modification, Condition, GroupCountQuery, AggregateQuery, GroupAggregateQuery, Aggregate, SortPart, DataClassPath, DataClassPathPartial, QueryPartial, DeepPartial, Fetcher, Brand } from '@lightningkite/lightning-server-simplified' +import type { CollectionUpdates, TestModel, Mask, UpdateRestrictions, ModelPermissions, Pair, TestInput, Uuid } from './models.ts' import type { Api } from './Api.ts' export class LiveApi implements Api { diff --git a/typed/src/test/kotlin/com/lightningkite/lightningserver/typed/sdk/generated/typescript/models.ts b/typed/src/test/kotlin/com/lightningkite/lightningserver/typed/sdk/generated/typescript/models.ts index 5dc3749e2..1da6dee1b 100644 --- a/typed/src/test/kotlin/com/lightningkite/lightningserver/typed/sdk/generated/typescript/models.ts +++ b/typed/src/test/kotlin/com/lightningkite/lightningserver/typed/sdk/generated/typescript/models.ts @@ -1,4 +1,4 @@ -import type { Query, MassModification, EntryChange, ListChange, Modification, Condition, GroupCountQuery, AggregateQuery, GroupAggregateQuery, Aggregate, SortPart, DataClassPath, DataClassPathPartial, QueryPartial, DeepPartial, Fetcher } from '@lightningkite/lightning-server-simplified' +import type { Query, MassModification, EntryChange, ListChange, Modification, Condition, GroupCountQuery, AggregateQuery, GroupAggregateQuery, Aggregate, SortPart, DataClassPath, DataClassPathPartial, QueryPartial, DeepPartial, Fetcher, Brand } from '@lightningkite/lightning-server-simplified' export interface CollectionUpdates { updates: Array @@ -11,11 +11,6 @@ export interface Mask { pairs: Array, Modification>> } -export enum Mode { - Blacklist = "Blacklist", - Whitelist = "Whitelist", -} - export interface ModelPermissions { create: Condition read: Condition @@ -31,25 +26,48 @@ export interface Pair { second: T1 } -export interface Part { - property: DataClassPathPartial - requires: Condition - limitedTo: Condition -} - export interface TestInput { id: number name: string } export interface TestModel { - _id: Uuid + _id: TestModel.ID name: string + statusInfo: TestModel.TestStatusInfo +} + +export namespace TestModel { + export type ID = Brand + + export enum Status { + Active = "Active", + Inactive = "Inactive", + Pending = "Pending", + } + + export interface TestStatusInfo { + status: TestModel.Status + updatedAt: number + } } export interface UpdateRestrictions { - mode: Mode - fields: Array> + mode: UpdateRestrictions.Mode + fields: Array> +} + +export namespace UpdateRestrictions { + export enum Mode { + Blacklist = "Blacklist", + Whitelist = "Whitelist", + } + + export interface Part { + property: DataClassPathPartial + requires: Condition + limitedTo: Condition + } } export type Uuid = string // kotlin.uuid.Uuid diff --git a/typed/src/test/kotlin/com/lightningkite/lightningserver/typed/sdk/generated/typescript/sdk.ts b/typed/src/test/kotlin/com/lightningkite/lightningserver/typed/sdk/generated/typescript/sdk.ts index 93c3e2ab4..a20a236d6 100644 --- a/typed/src/test/kotlin/com/lightningkite/lightningserver/typed/sdk/generated/typescript/sdk.ts +++ b/typed/src/test/kotlin/com/lightningkite/lightningserver/typed/sdk/generated/typescript/sdk.ts @@ -1,4 +1,4 @@ -import type { Query, MassModification, EntryChange, ListChange, Modification, Condition, GroupCountQuery, AggregateQuery, GroupAggregateQuery, Aggregate, SortPart, DataClassPath, DataClassPathPartial, QueryPartial, DeepPartial, Fetcher } from '@lightningkite/lightning-server-simplified' +import type { Query, MassModification, EntryChange, ListChange, Modification, Condition, GroupCountQuery, AggregateQuery, GroupAggregateQuery, Aggregate, SortPart, DataClassPath, DataClassPathPartial, QueryPartial, DeepPartial, Fetcher, Brand } from '@lightningkite/lightning-server-simplified' export interface CollectionUpdates { updates: Array @@ -11,11 +11,6 @@ export interface Mask { pairs: Array, Modification>> } -export enum Mode { - Blacklist = "Blacklist", - Whitelist = "Whitelist", -} - export interface ModelPermissions { create: Condition read: Condition @@ -31,25 +26,48 @@ export interface Pair { second: T1 } -export interface Part { - property: DataClassPathPartial - requires: Condition - limitedTo: Condition -} - export interface TestInput { id: number name: string } export interface TestModel { - _id: Uuid + _id: TestModel.ID name: string + statusInfo: TestModel.TestStatusInfo +} + +export namespace TestModel { + export type ID = Brand + + export enum Status { + Active = "Active", + Inactive = "Inactive", + Pending = "Pending", + } + + export interface TestStatusInfo { + status: TestModel.Status + updatedAt: number + } } export interface UpdateRestrictions { - mode: Mode - fields: Array> + mode: UpdateRestrictions.Mode + fields: Array> +} + +export namespace UpdateRestrictions { + export enum Mode { + Blacklist = "Blacklist", + Whitelist = "Whitelist", + } + + export interface Part { + property: DataClassPathPartial + requires: Condition + limitedTo: Condition + } } export type Uuid = string // kotlin.uuid.Uuid @@ -243,7 +261,7 @@ export interface Api { * * **Auth Requirements:** No Requirements * */ - detail(id: Uuid): Promise + detail(id: TestModel.ID): Promise /** * Upsert * @@ -251,7 +269,7 @@ export interface Api { * * **Auth Requirements:** No Requirements * */ - upsert(id: Uuid, input: TestModel): Promise + upsert(id: TestModel.ID, input: TestModel): Promise /** * Replace * @@ -259,7 +277,7 @@ export interface Api { * * **Auth Requirements:** No Requirements * */ - replace(id: Uuid, input: TestModel): Promise + replace(id: TestModel.ID, input: TestModel): Promise /** * Modify * @@ -267,7 +285,7 @@ export interface Api { * * **Auth Requirements:** No Requirements * */ - modify(id: Uuid, input: Modification): Promise + modify(id: TestModel.ID, input: Modification): Promise /** * Delete * @@ -275,7 +293,7 @@ export interface Api { * * **Auth Requirements:** No Requirements * */ - delete(id: Uuid): Promise + delete(id: TestModel.ID): Promise /** * Simplified Modify * @@ -283,7 +301,7 @@ export interface Api { * * **Auth Requirements:** No Requirements * */ - simplifiedModify(id: Uuid, input: Partial): Promise + simplifiedModify(id: TestModel.ID, input: Partial): Promise /** * Modify with Diff * @@ -291,7 +309,7 @@ export interface Api { * * **Auth Requirements:** No Requirements * */ - modifyWithDiff(id: Uuid, input: Modification): Promise> + modifyWithDiff(id: TestModel.ID, input: Modification): Promise> readonly default: { /** @@ -429,7 +447,7 @@ export interface Api { * * **Auth Requirements:** Authenticated * */ - detail(id: Uuid): Promise + detail(id: TestModel.ID): Promise /** * Upsert * @@ -437,7 +455,7 @@ export interface Api { * * **Auth Requirements:** Authenticated * */ - upsert(id: Uuid, input: TestModel): Promise + upsert(id: TestModel.ID, input: TestModel): Promise /** * Replace * @@ -445,7 +463,7 @@ export interface Api { * * **Auth Requirements:** Authenticated * */ - replace(id: Uuid, input: TestModel): Promise + replace(id: TestModel.ID, input: TestModel): Promise /** * Modify * @@ -453,7 +471,7 @@ export interface Api { * * **Auth Requirements:** Authenticated * */ - modify(id: Uuid, input: Modification): Promise + modify(id: TestModel.ID, input: Modification): Promise /** * Delete * @@ -461,7 +479,7 @@ export interface Api { * * **Auth Requirements:** Authenticated * */ - delete(id: Uuid): Promise + delete(id: TestModel.ID): Promise /** * Simplified Modify * @@ -469,7 +487,7 @@ export interface Api { * * **Auth Requirements:** Authenticated * */ - simplifiedModify(id: Uuid, input: Partial): Promise + simplifiedModify(id: TestModel.ID, input: Partial): Promise /** * Modify with Diff * @@ -477,7 +495,7 @@ export interface Api { * * **Auth Requirements:** Authenticated * */ - modifyWithDiff(id: Uuid, input: Modification): Promise> + modifyWithDiff(id: TestModel.ID, input: Modification): Promise> readonly notInlined: { /** @@ -626,7 +644,7 @@ export interface Api { * * **Auth Requirements:** Authenticated * */ - detail(id: Uuid): Promise + detail(id: TestModel.ID): Promise /** * Upsert * @@ -634,7 +652,7 @@ export interface Api { * * **Auth Requirements:** Authenticated * */ - upsert(id: Uuid, input: TestModel): Promise + upsert(id: TestModel.ID, input: TestModel): Promise /** * Replace * @@ -642,7 +660,7 @@ export interface Api { * * **Auth Requirements:** Authenticated * */ - replace(id: Uuid, input: TestModel): Promise + replace(id: TestModel.ID, input: TestModel): Promise /** * Modify * @@ -650,7 +668,7 @@ export interface Api { * * **Auth Requirements:** Authenticated * */ - modify(id: Uuid, input: Modification): Promise + modify(id: TestModel.ID, input: Modification): Promise /** * Delete * @@ -658,7 +676,7 @@ export interface Api { * * **Auth Requirements:** Authenticated * */ - delete(id: Uuid): Promise + delete(id: TestModel.ID): Promise /** * Simplified Modify * @@ -666,7 +684,7 @@ export interface Api { * * **Auth Requirements:** Authenticated * */ - simplifiedModify(id: Uuid, input: Partial): Promise + simplifiedModify(id: TestModel.ID, input: Partial): Promise /** * Modify with Diff * @@ -674,7 +692,7 @@ export interface Api { * * **Auth Requirements:** Authenticated * */ - modifyWithDiff(id: Uuid, input: Modification): Promise> + modifyWithDiff(id: TestModel.ID, input: Modification): Promise> readonly notInlined: { /** @@ -824,7 +842,7 @@ export interface Api { * * **Auth Requirements:** Authenticated * */ - detail(id: Uuid): Promise + detail(id: TestModel.ID): Promise /** * Upsert * @@ -832,7 +850,7 @@ export interface Api { * * **Auth Requirements:** Authenticated * */ - upsert(id: Uuid, input: TestModel): Promise + upsert(id: TestModel.ID, input: TestModel): Promise /** * Replace * @@ -840,7 +858,7 @@ export interface Api { * * **Auth Requirements:** Authenticated * */ - replace(id: Uuid, input: TestModel): Promise + replace(id: TestModel.ID, input: TestModel): Promise /** * Modify * @@ -848,7 +866,7 @@ export interface Api { * * **Auth Requirements:** Authenticated * */ - modify(id: Uuid, input: Modification): Promise + modify(id: TestModel.ID, input: Modification): Promise /** * Delete * @@ -856,7 +874,7 @@ export interface Api { * * **Auth Requirements:** Authenticated * */ - delete(id: Uuid): Promise + delete(id: TestModel.ID): Promise /** * Simplified Modify * @@ -864,7 +882,7 @@ export interface Api { * * **Auth Requirements:** Authenticated * */ - simplifiedModify(id: Uuid, input: Partial): Promise + simplifiedModify(id: TestModel.ID, input: Partial): Promise /** * Modify with Diff * @@ -872,7 +890,7 @@ export interface Api { * * **Auth Requirements:** Authenticated * */ - modifyWithDiff(id: Uuid, input: Modification): Promise> + modifyWithDiff(id: TestModel.ID, input: Modification): Promise> readonly notInlined: { /** @@ -1031,7 +1049,7 @@ export interface Api { * * **Auth Requirements:** No Requirements * */ - detail(id: Uuid): Promise + detail(id: TestModel.ID): Promise /** * Upsert * @@ -1039,7 +1057,7 @@ export interface Api { * * **Auth Requirements:** No Requirements * */ - upsert(id: Uuid, input: TestModel): Promise + upsert(id: TestModel.ID, input: TestModel): Promise /** * Replace * @@ -1047,7 +1065,7 @@ export interface Api { * * **Auth Requirements:** No Requirements * */ - replace(id: Uuid, input: TestModel): Promise + replace(id: TestModel.ID, input: TestModel): Promise /** * Modify * @@ -1055,7 +1073,7 @@ export interface Api { * * **Auth Requirements:** No Requirements * */ - modify(id: Uuid, input: Modification): Promise + modify(id: TestModel.ID, input: Modification): Promise /** * Delete * @@ -1063,7 +1081,7 @@ export interface Api { * * **Auth Requirements:** No Requirements * */ - delete(id: Uuid): Promise + delete(id: TestModel.ID): Promise /** * Simplified Modify * @@ -1071,7 +1089,7 @@ export interface Api { * * **Auth Requirements:** No Requirements * */ - simplifiedModify(id: Uuid, input: Partial): Promise + simplifiedModify(id: TestModel.ID, input: Partial): Promise /** * Modify with Diff * @@ -1079,7 +1097,7 @@ export interface Api { * * **Auth Requirements:** No Requirements * */ - modifyWithDiff(id: Uuid, input: Modification): Promise> + modifyWithDiff(id: TestModel.ID, input: Modification): Promise> } readonly rest2: { /** @@ -1209,7 +1227,7 @@ export interface Api { * * **Auth Requirements:** No Requirements * */ - detail(id: Uuid): Promise + detail(id: TestModel.ID): Promise /** * Upsert * @@ -1217,7 +1235,7 @@ export interface Api { * * **Auth Requirements:** No Requirements * */ - upsert(id: Uuid, input: TestModel): Promise + upsert(id: TestModel.ID, input: TestModel): Promise /** * Replace * @@ -1225,7 +1243,7 @@ export interface Api { * * **Auth Requirements:** No Requirements * */ - replace(id: Uuid, input: TestModel): Promise + replace(id: TestModel.ID, input: TestModel): Promise /** * Modify * @@ -1233,7 +1251,7 @@ export interface Api { * * **Auth Requirements:** No Requirements * */ - modify(id: Uuid, input: Modification): Promise + modify(id: TestModel.ID, input: Modification): Promise /** * Delete * @@ -1241,7 +1259,7 @@ export interface Api { * * **Auth Requirements:** No Requirements * */ - delete(id: Uuid): Promise + delete(id: TestModel.ID): Promise /** * Simplified Modify * @@ -1249,7 +1267,7 @@ export interface Api { * * **Auth Requirements:** No Requirements * */ - simplifiedModify(id: Uuid, input: Partial): Promise + simplifiedModify(id: TestModel.ID, input: Partial): Promise /** * Modify with Diff * @@ -1257,7 +1275,7 @@ export interface Api { * * **Auth Requirements:** No Requirements * */ - modifyWithDiff(id: Uuid, input: Modification): Promise> + modifyWithDiff(id: TestModel.ID, input: Modification): Promise> } } } diff --git a/typed/src/test/kotlin/com/lightningkite/lightningserver/typed/sdk/server.kt b/typed/src/test/kotlin/com/lightningkite/lightningserver/typed/sdk/server.kt index 55acd5619..a71bb1ac8 100644 --- a/typed/src/test/kotlin/com/lightningkite/lightningserver/typed/sdk/server.kt +++ b/typed/src/test/kotlin/com/lightningkite/lightningserver/typed/sdk/server.kt @@ -89,9 +89,27 @@ private val testEndpoint = explicitApiHttpHandler, HasId<*>?, @Serializable data class TestModel( - override val _id: Uuid = Uuid.random(), + override val _id: ID = ID(), val name: String, -) : HasId + val statusInfo: TestStatusInfo = TestStatusInfo(), +) : HasId { + @JvmInline + @Serializable + value class ID(override val raw: Uuid = Uuid.random()) : TypedId { + override fun toString(): String = raw.toString() + } + + enum class Status { + Active, + Inactive, + Pending, + } + @Serializable + data class TestStatusInfo( + val status: Status = Status.Active, + val updatedAt: Int = 0, + ) +} object Module : ServerBuilder() { val info = Server.database.modelInfo( From ef7d6702b834d9d17961f45bf2c9e5b5e4762929 Mon Sep 17 00:00:00 2001 From: Wesley Edwards Date: Fri, 17 Jul 2026 11:54:43 -0600 Subject: [PATCH 6/6] update sdk generator for ts sdk --- .../typed/sdk/TypescriptFetcherSdk.kt | 272 ++++++++---------- 1 file changed, 117 insertions(+), 155 deletions(-) diff --git a/typed/src/main/kotlin/com/lightningkite/lightningserver/typed/sdk/TypescriptFetcherSdk.kt b/typed/src/main/kotlin/com/lightningkite/lightningserver/typed/sdk/TypescriptFetcherSdk.kt index 51b75d03d..8d67f1d8d 100644 --- a/typed/src/main/kotlin/com/lightningkite/lightningserver/typed/sdk/TypescriptFetcherSdk.kt +++ b/typed/src/main/kotlin/com/lightningkite/lightningserver/typed/sdk/TypescriptFetcherSdk.kt @@ -210,20 +210,21 @@ public class TypescriptFetcherSdk( val namespaces = linkedMapOf>() val topLevelDeclarations = ArrayList>() - fun String.topLevelDeclarationName(): String = substringBefore('<').substringBefore('.') - - fun Appendable.appendNamespace( - typeName: String, - declaration: Appendable.(localName: String, depth: Int) -> Unit, - ): Boolean { - val namespace = typeName.substringBefore('.', missingDelimiterValue = "") - if (namespace.isBlank()) { - return true - } else { + fun Appendable.appendDecl( + name: String, + emit: Appendable.(String, Int) -> Unit, + ): Pair? { + val namespace = name.substringBefore('.', "") + + if (namespace.isNotEmpty()) { namespaces.getOrPut(namespace) { ArrayList() } += buildString { - declaration(typeName.substringAfter('.'), 1) + emit(name.substringAfter('.'), 1) } - return false + return null + } +// Top Level Declaration Name + return name.substringBefore('<').substringBefore('.') to buildString { + emit(name, 0) } } @@ -238,164 +239,132 @@ public class TypescriptFetcherSdk( appendLine() } - fun renderType(type: KSerializer<*>): Pair? { - when (type.descriptor.kind) { - StructureKind.CLASS -> { - val genericMap: Map = type - .getGenerics() - ?.withIndex() - ?.associate { (index, value) -> - value.tsType() to "T${if (index > 0) index else ""}" - } - ?: emptyMap() - - fun String.replaceGenerics(): String = - genericMap.entries - .sortedByDescending { it.key.length } - .fold(this) { acc, (old, new) -> acc.replace(old, new) } - - if (type.descriptor.isInline) { - val valueType = - type.serializableProperties?.firstOrNull()?.serializer?.tsType()?.replaceGenerics() - val name = type.tsType().replaceGenerics() - if (!StringBuilder().appendNamespace(name) { localName, depth -> - appendIdtLine(depth, "export type $localName = Brand<${valueType}, \"$name\">") - }) return null - return name.topLevelDeclarationName() to buildString { - appendLine("export type $name = Brand<${valueType}, \"$name\">") - } - } else { - val properties = type - .serializableProperties?.map { it.serializer } - ?: type.childSerializersOrNull()?.toList() - ?: emptyList() - - val name = type.tsType().replaceGenerics() - if (!StringBuilder().appendNamespace(name) { localName, depth -> - appendIdtLine(depth, "export interface $localName {") - for ((idx, prop) in properties.withIndex()) { - appendIdtLine(depth + 1, "${type.descriptor.getElementName(idx)}: ${prop.tsType().replaceGenerics()}") - } - - appendIdtLine(depth, "}") - }) return null - return name.topLevelDeclarationName() to buildString { - appendLine("export interface $name {") - for ((idx, prop) in properties.withIndex()) { - appendIdtLine(1, "${type.descriptor.getElementName(idx)}: ${prop.tsType().replaceGenerics()}") - } - - appendLine("}") - } + fun renderType(type: KSerializer<*>): Pair? = when (type.descriptor.kind) { + StructureKind.CLASS -> { + val genericMap: Map = type + .getGenerics() + ?.withIndex() + ?.associate { (index, value) -> + value.tsType() to "T${if (index > 0) index else ""}" } + ?: emptyMap() + + fun String.replaceGenerics(): String = + genericMap.entries + .sortedByDescending { it.key.length } + .fold(this) { acc, (old, new) -> acc.replace(old, new) } + + if (type.descriptor.isInline) { + val valueType = + type.serializableProperties?.firstOrNull()?.serializer?.tsType()?.replaceGenerics() + val name = type.tsType().replaceGenerics() + appendDecl(name, { localName, depth -> + appendIdtLine(depth, "export type $localName = Brand<${valueType}, \"$name\">") + }) + } else { + val properties = type + .serializableProperties?.map { it.serializer } + ?: type.childSerializersOrNull()?.toList() + ?: emptyList() + + val name = type.tsType().replaceGenerics() + + appendDecl(name, { localName, depth -> + appendIdtLine(depth, "export interface $localName {") + for ((idx, prop) in properties.withIndex()) { + appendIdtLine( + depth + 1, + "${type.descriptor.getElementName(idx)}: ${prop.tsType().replaceGenerics()}" + ) + } - + appendIdtLine(depth, "}") + }) } + } - SerialKind.ENUM -> { - val typeName = type.tsType() - if (erasableTypes) { - if (!StringBuilder().appendNamespace(typeName) { localName, depth -> - appendIdt(depth) - append("export type $localName = ") - for (index in 0 until type.descriptor.elementsCount) { - val name = type.descriptor.getElementName(index) - append(if (index == 0) "\"$name\"" else "| \"$name\"") - } - appendLine() - }) return null - return typeName.topLevelDeclarationName() to buildString { - append("export type $typeName = ") - for (index in 0 until type.descriptor.elementsCount) { - val name = type.descriptor.getElementName(index) - append(if (index == 0) "\"$name\"" else "| \"$name\"") + SerialKind.ENUM -> { + val typeName = type.tsType() + if (erasableTypes) { + appendDecl(typeName, { localName, depth -> + appendIdt(depth) + append("export type $localName = ") + for (index in 0 until type.descriptor.elementsCount) { + val name = type.descriptor.getElementName(index) + append(if (index == 0) "\"$name\"" else "| \"$name\"") + } + appendLine() + }) + } else { + val typeName = type.tsType(); + appendDecl(typeName, { localName, depth -> + appendIdtLine(depth, "export enum $localName {") + for (index in 0 until type.descriptor.elementsCount) { + appendIdt(depth + 1) + val name = type.descriptor.getElementName(index) + name.forEachIndexed { idx, it -> + if ((idx == 0 && it.isJavaIdentifierStart()) || (idx != 0 && it.isJavaIdentifierPart())) + append(it) + else + append('_') } + append(" = \"$name\",") appendLine() } - } else { - if (!StringBuilder().appendNamespace(typeName) { localName, depth -> - appendIdtLine(depth, "export enum $localName {") - for (index in 0 until type.descriptor.elementsCount) { - appendIdt(depth + 1) - val name = type.descriptor.getElementName(index) - name.forEachIndexed { idx, it -> - if ((idx == 0 && it.isJavaIdentifierStart()) || (idx != 0 && it.isJavaIdentifierPart())) - append(it) - else - append('_') - } - append(" = \"$name\",") - appendLine() - } - - appendIdtLine(depth, "}") - }) return null - return typeName.topLevelDeclarationName() to buildString { - appendLine("export enum $typeName {") - for (index in 0 until type.descriptor.elementsCount) { - appendIdt(1) - val name = type.descriptor.getElementName(index) - name.forEachIndexed { idx, it -> - if ((idx == 0 && it.isJavaIdentifierStart()) || (idx != 0 && it.isJavaIdentifierPart())) - append(it) - else - append('_') - } - append(" = \"$name\",") - appendLine() - } - appendLine("}") - } - } + appendIdtLine(depth, "}") + }) } + } - PrimitiveKind.STRING -> { - val name = type.descriptor.simpleSerialName - if (name != "String" && stringSerialNames.add(name)) { - return name to buildString { - appendLine( - "export type $name = string // ${type.descriptor.serialName}" - ) - } + PrimitiveKind.STRING -> { + val name = type.descriptor.simpleSerialName + if (name != "String" && stringSerialNames.add(name)) { + name to buildString { + appendLine( + "export type $name = string // ${type.descriptor.serialName}" + ) } - return null - } + } else null + } - is PolymorphicKind -> { - val options = type.sealedOptionsOrNull() ?: continue + is PolymorphicKind -> { + val options = type.sealedOptionsOrNull() ?: return null + val genericMap: Map = type + .getGenerics() + ?.withIndex() + ?.associate { (index, value) -> + value.tsType() to "T${if (index > 0) index else ""}" + } + ?: emptyMap() - val genericMap: Map = type - .getGenerics() - ?.withIndex() - ?.associate { (index, value) -> - value.tsType() to "T${if (index > 0) index else ""}" - } - ?: emptyMap() + fun String.replaceGenerics(): String = + genericMap.entries.fold(this) { acc, (old, new) -> acc.replace(old, new) } - fun String.replaceGenerics(): String = - genericMap.entries.fold(this) { acc, (old, new) -> acc.replace(old, new) } + // A discriminated union of the subtypes. App `@Serializable sealed` types serialize + // flat with a "type" discriminator ({ "type": "", ...subtype fields }); framework + // wrapper types serialize as { "": }. Each subtype is emitted as its own + // interface, so the union just references those by name. - // A discriminated union of the subtypes. App `@Serializable sealed` types serialize - // flat with a "type" discriminator ({ "type": "", ...subtype fields }); framework - // wrapper types serialize as { "": }. Each subtype is emitted as its own - // interface, so the union just references those by name. + val typeName = type.tsType().replaceGenerics() + appendDecl(typeName, { localName, depth -> val wrapper = type.isWrapperSealed() - append("export type ${type.tsType().replaceGenerics()} =") + appendIdt(depth) + append("export type ${localName} =") if (options.isEmpty()) { appendLine(" never") } else { appendLine() for (option in options) { val sub = option.serializer.tsType().replaceGenerics() - if (wrapper) appendIdtLine(1, "| { \"${option.name}\": $sub }") - else appendIdtLine(1, "| ({ type: \"${option.name}\" } & $sub)") + if (wrapper) appendIdtLine(depth + 1, "| { \"${option.name}\": $sub }") + else appendIdtLine(depth + 1, "| ({ type: \"${option.name}\" } & $sub)") } } - } - - else -> return null + }) } + + else -> null } for (type in types) { @@ -557,16 +526,12 @@ public class TypescriptFetcherSdk( .sortedBy { it.descriptor.simpleSerialName } .distinctBy { it.tsType().substringBefore("<") } // Distinct by generics .filter { - if(it.descriptor.isInline) true - else when (it.descriptor.kind) { + when (it.descriptor.kind) { SerialKind.ENUM -> true - // Inline value classes serialize as their underlying primitive (see tsType), so they get no - // standalone type definition and must not be emitted/imported as model types. - StructureKind.CLASS if (it !is MySealedClassSerializer && !it.descriptor.isInline) -> true + StructureKind.CLASS if (it !is MySealedClassSerializer) -> true PrimitiveKind.STRING if (it.descriptor.simpleSerialName != "String") -> true // Sealed/polymorphic types are emitted as TS discriminated unions (see writeTypeDefinitions). is PolymorphicKind -> true - else -> false } } @@ -637,10 +602,7 @@ public class TypescriptFetcherSdk( } private val SerialDescriptor.simpleSerialName: String - get() { - val fqn = serialName.substringBefore('<').substringBefore('/').removeSuffix("?") - return fqn.split('.').filter { it.first().isUpperCase() }.joinToString("") - } + get() = serialName.substringBefore('<').substringBefore('/').substringAfterLast('.').removeSuffix("?") private val SerialDescriptor.nestedTsTypeName: String? get() {