Base compartida para procesadores de anotaciones/símbolos con arquitectura hexagonal. Extrae, una sola vez, la parte de un processor que no es específica del dominio y que se reescribe (con bugs sutiles) en cada uno: modelo neutral de tipos, resolución de tipos, diagnósticos que acumulan, y un contrato de emisión neutral. Con dos frontends sobre el mismo modelo:
kspkit— frontend KSP (Kotlin Symbol Processing).aptkit— frontend APT (javac annotation processing, para clases Java).
El nombre es deliberado: genkit-* es agnóstico del frontend. Un consumidor de solo-Java
(vía aptkit) nunca depende de un artefacto llamado "ksp", y viceversa.
La usan kaudit y kvalid, y kmapx para su modelo de tipos.
Para construir tu propio processor sobre genkit:
- Crea tu processor — el flujo completo con un ejemplo mínimo (anotación → dominio → emisor → processor KSP) + cómo testear sin compilar.
- Modelo y puertos — referencia de
genkit-model(TypeRef,ClassModel,AnnotationArg…) ygenkit-ports(TypeResolver,DiagnosticReporter,CodeWriter) + los dobles en memoria. - Frontends (KSP / APT) — cablear
kspkit(Kotlin) yaptkit(Java), y la paridad entre ambos. - Sitio (MkDocs Material):
mkdocs serve. Ejemplos completos: kaudit y kvalid.
v0.1 (io.github.kuroxbyte:genkit-*, kspkit, aptkit; Apache-2.0). 23 tests en verde
(unidad + compile-testing real con kctfork KSP2 y javac ToolProvider). Probado por tres
consumidores: la "prueba de fuego" — kvalid reutilizó los puertos de kaudit sin deformarlos.
Separar dominio (puro, testeable sin compilar) de infraestructura (KSP, APT, Poet):
- El dominio de una librería depende solo de
genkit-modelygenkit-ports. - Un adaptador de entrada (
kspkitpara KSP,aptkitpara APT) traduce sus símbolos a un modelo neutral: ningúnKSNodenijavax.lang.model.Elementsobrevive a la frontera. - La salida es un contrato neutral (
GeneratedFile, texto); KotlinPoet/JavaPoet viven aislados engenkit-emit.
El pago: un adaptador K2 futuro (plugin de compilador) reutilizaría el 100% del dominio; y el dominio se testea con dobles en memoria → suites de cientos de casos en milisegundos.
| Módulo | Rol | Dependencias |
|---|---|---|
genkit-model |
Modelo neutral: TypeRef/TypeKind, ClassModel/PropertyModel/…, AnnotationModel/AnnotationArg, SourceRef. Agnóstico del frontend. |
CERO |
genkit-ports |
Puertos: TypeResolver, DiagnosticReporter, CodeWriter + GeneratedFile. Dobles en memoria vía test-fixtures. |
model |
genkit-emit |
Puentes FileSpec.toGeneratedFile() (KotlinPoet) y JavaFile.toGeneratedFile() (JavaPoet). Aísla los toolkits de emisión. |
ports, KotlinPoet, JavaPoet |
kspkit |
Frontend KSP: KspTranslator (KSP→modelo), KspTypeResolver, KspDiagnosticReporter (sobre KSPLogger), KspCodeWriter (sobre CodeGenerator), puente SourceRef↔KSNode. |
model, ports, KSP |
aptkit |
Frontend APT (Java): AptTranslator (javax.lang.model→modelo, records + POJOs), AptTypeResolver, AptDiagnosticReporter (sobre Messager), AptCodeWriter (sobre Filer). |
model, ports, JDK |
interface TypeResolver {
fun resolve(ref: TypeRef): ClassModel?
fun isAnnotatedWith(ref: TypeRef, fqName: String): Boolean
}
interface DiagnosticReporter { // acumula, no aborta al primer error
fun error(code: String, message: String, at: SourceRef? = null)
fun warn(code: String, message: String, at: SourceRef? = null)
fun hasErrors(): Boolean
}
fun interface CodeWriter { fun write(file: GeneratedFile, origins: Set<SourceRef>) }
data class GeneratedFile(val packageName: String, val fileName: String, val content: String)DiagnosticReporter usa códigos como String: cada librería define su propio espacio
(kaudit.key.nullable, size.max). Los dobles en memoria se consumen con
testImplementation(testFixtures("io.github.kuroxbyte:genkit-ports")).
*-core de cada librería (y genkit-model/genkit-ports) no compilan con dependencia a
KSP ni Poet:
- Gradle — no se declara la dependencia.
- Konsist — un test de arquitectura falla si algún archivo importa
com.google.devtools.kspocom.squareup.kotlinpoet.
// settings.gradle.kts del consumidor
includeBuild("../genkit") // dev: substitución por proyecto
// build.gradle.kts del *-core (solo el modelo neutral)
dependencies {
api("io.github.kuroxbyte:genkit-model:0.1.0")
api("io.github.kuroxbyte:genkit-ports:0.1.0")
testImplementation(testFixtures("io.github.kuroxbyte:genkit-ports:0.1.0"))
}
// el *-processor (KSP) añade io.github.kuroxbyte:kspkit
// la variante *-apt (Java) añade io.github.kuroxbyte:aptkitEn release se usa la versión publicada; genkit se publica antes que sus consumidores.
./gradlew buildRequisitos: JDK 17+.