Skip to content

Commit e0334cb

Browse files
dmealingclaude
andauthored
feat: typed value-object jsonb columns work end-to-end across all ports (#187)
* feat(jsonb): typed value-object jsonb columns work end-to-end across all ports Typed @objectref value-object columns stored as jsonb (field.object @storage:jsonb, single AND @isarray) now write+read correctly through every port's persistence layer, gated by a new cross-port array-of-VO roundtrip scenario. Three genuine, runtime-proven bugs fixed (the array-of-VO path was untested everywhere, which is how they shipped): Kotlin (codegen-kotlin) — REFACTOR to Jackson, drop decorative @serializable. The port stamped kotlinx-serialization @serializable on every entity/value/ projection data class, and the typed jsonb column emitted Json.encodeToString(VO.serializer(), ...) — which requires the kotlinx compiler plugin, and the moment that plugin is enabled EVERY class with a UUID/Instant/BigDecimal/URI/Inet/temporal field fails to compile (kotlinx has no serializer for those java.* types). No consumer ever used kotlinx (all Jackson); @serializable was decorative. Now: entity/value/projection classes are plain data classes (Jackson-compatible); the typed jsonb + field.map columns use a generated per-package MetaJsonbMapper.kt (Jackson ObjectMapper with kotlinModule + JavaTimeModule) via Exposed's serialize/deserialize lambda overload — no compiler plugin, matching Java(Gson)/C#(System.Text.Json)/TS/ Python. @serializable kept only on prompt payloads + enums (which genuinely kotlinx-decode). New gate: a compile-without-the-plugin + Testcontainers-PG roundtrip of a GENERATED typed-jsonb table (the missing gate that let the broken form ship). Supersedes the unmerged fix/kotlin-exposed-jsonb-object-column. C# (MetaObjects.Codegen) — array-of-VO jsonb was BROKEN (EF model finalization threw "ICollection must be a non-interface reference type"). DbContextGenerator now emits .OwnsMany (not .OwnsOne) when field.ResolvedIsArray(); a coupled nullability defect (a non-null List initializer couldn't represent a NULL jsonb column, conflating empty-[] with null) is fixed to mirror single-VO nullability. Java (OMDB) — array-of-VO jsonb was a GAP (read threw "Expected BEGIN_OBJECT but was BEGIN_ARRAY"). GenericSQLDriver.deserializeJsonb now branches on isArray (TypeToken<List<VO>>) and parseField routes through setObjectArray; the integration-tests RoundtripWriter authors a List<ValueObject> insert value. Oracle: fixtures/persistence-conformance — a Label VO + a labels array-of-VO jsonb field on AllTypes (regenerated canonical schema); roundtrip scenarios exercise 2-element / empty-[] / 1-element arrays + the null case. TS was already correct (27/0); it proved the fixture and disproved nothing. All ports green: TS 27/0, C# 1402/0, Java OMDB 48/0 + persistence 24/0, Kotlin 272/0 + integration 97/0 (all Testcontainers PG). Co-Authored-By: Claude <noreply@anthropic.com> * refactor(codegen-kotlin): dedup jsonb support-file emit + codec FQN (simplifier) Quality-only follow-up from the 4-angle simplifier pass on the jsonb work (reuse/simplification/efficiency/altitude — efficiency + altitude were clean, C# + Java clean; all findings were Kotlin generator dedup): - Extract emitSupportFile(pkg, outRoot, fileName, imports, block): the jsonb / inet-uri / instant-tz per-package support-file emitters were three byte-identical copies of the buildString + resolve + writeString scaffold. One helper now, each emitter a one-liner. - Use PackageMapping.toKotlin(target.name) instead of hand-splitting splitFqn then rejoining "$p.$s" (byte-identical: toKotlin is ::→. on the whole FQN) at the two new VO-FQN sites (buildObjectColumns decode + mapValueTypeFqn). - Fold entityNeedsJacksonMapper's two field passes into one loop (the flattened sub-field field.map check moves into the ObjectField branch — same behavior). codegen-kotlin 272/0 — snapshots still byte-match (behavior-preserving). Co-Authored-By: Claude <noreply@anthropic.com> * no-mistakes(review): docs(kotlin): document Jackson jsonb deps, drop stale @serializable claims * no-mistakes(test): fix Python jsonb write codec for array-of-VO roundtrip * no-mistakes(document): docs: sync Kotlin Jackson jsonb + array-of-VO roundtrip gaps * no-mistakes(document): docs(kotlin): fix stale test count and @serializable example --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 644d27f commit e0334cb

61 files changed

Lines changed: 932 additions & 151 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,13 @@ this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
99

1010
### Added
1111
- **`@convert` on `origin.passthrough` — acknowledge a deliberate passthrough type change (#185).** A new optional boolean attr. Absent/false (the default), a passthrough is type-preserving (see Changed, below); `@convert: true` opts a field out of the type-equality check when its type intentionally differs from its `@from` source. It is an **acknowledgement only — it does NOT generate a cast**; the value flows through unchanged and the consumer owns any coercion. Real type-converting projections remain `origin.expression`'s job (#159). Registered on `origin.passthrough` in all five ports (cross-port registry-conformance gated).
12+
- **Typed value-object jsonb columns work end-to-end across all persistence ports (single + array-of-VO).** A `field.object @storage:jsonb` column — a single value-object OR an `@isArray` array-of-VO — now round-trips through every port's runtime/ORM write+read codec (TS Kysely, C# EF Core, Java OMDB/Gson, Kotlin Exposed, Python pg8000). Gated by a new array-of-VO dimension in the persistence-conformance `AllTypes` `op: roundtrip` scenario: a `labels` column (`field.object @isArray @storage:jsonb`) written as a 2-element, empty-`[]` (≠ `null`), and single-element array across three rows — the gap that had let a non-compiling / wrong array serializer ship in three ports. The single-VO jsonb path was already cross-port green; this closes the array-of-VO half.
13+
14+
### Fixed
15+
- **C# / Java / Python array-of-VO jsonb write+read codecs.** C# EF Core model finalization threw `'ICollection must be a non-interface reference type'` on an `@isArray` `field.object @storage:jsonb` column — `DbContextGenerator` now emits `.OwnsMany(...).ToJson(...)` when `field.ResolvedIsArray()` (the `EntityGenerator` emits `ICollection<VO>`), with a coupled empty-`[]`-vs-`null` nullability fix. Java OMDB threw `Expected BEGIN_OBJECT but was BEGIN_ARRAY``GenericSQLDriver.deserializeJsonb` now branches on `isArray` (target `TypeToken.getParameterized(List.class, VO)` via `jsonbTargetType`) and the read path stores the resulting `List` through `setObjectArray` (not the scalar `setObject`). Python's `_coerce_write_value` now `json.dumps`s both dict and list jsonb-storage `field.object`/`field.map` values to a JSON text string — pg8000 binds a native `dict` to jsonb fine, but adapts a native `list` as a Postgres ARRAY literal (`{...,...}`) which the JSONB column rejects with `22P02`.
1216

1317
### Changed
18+
- **Kotlin codegen moved to Jackson for typed jsonb columns; entity/value/projection classes dropped `@Serializable`.** `KotlinExposedTableGenerator` now emits a per-package `MetaJsonbMapper.kt` — a `com.fasterxml.jackson.databind.ObjectMapper` (`kotlinModule()` + `JavaTimeModule()`, `WRITE_DATES_AS_TIMESTAMPS` disabled) — that the generated `jsonb()` column codecs read/write through (a `TypeReference<List<VO>>` captures the array-of-VO generic). Jackson (not kotlinx) is the codec precisely so generated entity/value/projection data classes carry **NO `@Serializable`** and need **NO `kotlin("plugin.serialization")` compiler plugin**: a kotlinx `VO.serializer()` would require the plugin, and once it is on, every VO carrying a `java.util.UUID` / `java.time.*` / `java.math.BigDecimal` / `java.net.*` field fails to compile (kotlinx has no serializer for those `java.*` types). `@Serializable` is **kept** only on prompt payloads + enums (genuinely kotlinx-decoded by the FR-006 output parser). Consumers generating any typed jsonb/`field.map` column add `jackson-databind` + `jackson-module-kotlin` + `jackson-datatype-jsr310` (documented in `docs/ports/kotlin.md` + `codegen-kotlin/KNOWN_GAPS.md`); the open-bag `field.string @dbColumnType:jsonb` column stays on the kotlinx `JsonElement` lane. New gate: compile-WITHOUT-the-serialization-plugin + Testcontainers-PG roundtrip of a GENERATED typed-jsonb table (`GeneratedTypedJsonbRoundTripTest`).
1419
- **BREAKING — `origin.passthrough` is now type-preserving: a passthrough field must match its `@from` source's type (#185).** A field carrying `origin.passthrough @from: "Entity.field"` forwards another field's value unchanged, so its declared `field.<subType>` and array-ness must be identical to the resolved source field. A divergence now fails load with `ERR_PASSTHROUGH_TYPE_MISMATCH` (e.g. a `field.uuid` source surfaced by a projection as `field.string` — the exact mismodeling that forces hand-written `String↔UUID` bridging and defeats a UUID migration). The check compares **subType and array-ness only — nullability is deliberately not judged** (a view over an outer join legitimately widens `NOT NULL` → nullable). Opt out of a deliberate type change with `@convert: true` (see Added). This **generalizes and retires** the narrow, stored-proc-parameter-ref-only `ERR_PARAMETER_REF_PASSTHROUGH_TYPE_MISMATCH` (FR-015) into one host-agnostic invariant covering projections, entities, values, and parameter refs alike. Enforced in the loader/verify in all five ports (TS / Python / C# / Java / Kotlin), cross-port conformance-gated. **Migration:** if load newly fails, the error names both the declared type and the source type — declare the source type (usually the fix — the projection was wrong), or add `@convert: true` if the divergence is intentional.
1520

1621
## [0.15.15] — 2026-07-07

CLAUDE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ _Last refreshed 2026-07-04._
3232
**Cross-port conformance corpora** (every port runs the shared corpus):
3333
- Metamodel: `fixtures/conformance/` (~90 fixtures). TS / C# / Java / Python all green.
3434
- Render: `fixtures/render-conformance/`. TS / C# / Java / Kotlin / Python byte-identical.
35-
- Persistence: `fixtures/persistence-conformance/`. **Query** scenarios run on every port (TS / C# / Java / Kotlin / Python), each provisioning its test DB by executing the committed, TS-produced `canonical/schema.postgres.sql` (Postgres only — Derby dropped for the cross-port query corpus, ADR-0015). The **migration** scenarios are exercised by **TS only** (TS owns schema migrations). **The corpus now gates WRITES, not just reads (SP-H):** an `op: roundtrip` scenario type INSERTs through each port's runtime/ORM write codec (NOT raw SQL), reads the row back, and asserts the wire-normalized value. The `AllTypes` entity (`roundtrip-all-types.yaml`) carries one field of **every** persistable `field.*` subtype — string/int/long/double/float/decimal/boolean/date/time/timestamp(+tz)/currency/enum/uuid/object — so every subtype write+read round-trips through every port against Testcontainers PG. (`field.byte`/`field.short`/`field.class` were cut as non-functional registration-only stubs — the matrix tracks only genuinely-supported subtypes; see `fixtures/registry-conformance/README.md` → "Per-subtype write-round-trip matrix".)
35+
- Persistence: `fixtures/persistence-conformance/`. **Query** scenarios run on every port (TS / C# / Java / Kotlin / Python), each provisioning its test DB by executing the committed, TS-produced `canonical/schema.postgres.sql` (Postgres only — Derby dropped for the cross-port query corpus, ADR-0015). The **migration** scenarios are exercised by **TS only** (TS owns schema migrations). **The corpus now gates WRITES, not just reads (SP-H):** an `op: roundtrip` scenario type INSERTs through each port's runtime/ORM write codec (NOT raw SQL), reads the row back, and asserts the wire-normalized value. The `AllTypes` entity (`roundtrip-all-types.yaml`) carries one field of **every** persistable `field.*` subtype — string/int/long/double/float/decimal/boolean/date/time/timestamp(+tz)/currency/enum/uuid/object — plus an **array-of-VO** `field.object @isArray @storage:jsonb` column (`labels`, written as 2-element / empty-`[]` / single-element arrays across the three rows) — so every subtype write+read (incl. the array-of-value-object jsonb codec) round-trips through every port against Testcontainers PG. (`field.byte`/`field.short`/`field.class` were cut as non-functional registration-only stubs — the matrix tracks only genuinely-supported subtypes; see `fixtures/registry-conformance/README.md` → "Per-subtype write-round-trip matrix".)
3636
- API-contract: `fixtures/api-contract-conformance/`. TS / C# / Java / Kotlin / Python all 20/20 — each port runs **two lanes**: a hand-rolled reference server AND its **generated** API artifact booted over HTTP (the deployed controller/routes; TS+C# full-stack vs Testcontainers PG, Java/Kotlin/Python generated controller + in-memory repo behind the consumer seam). The generated fan-out found 10 real deployment bugs golden snapshots missed.
3737
- YAML / verify corpora green across the ports that ship those layers.
3838

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ building blocks are complete in all five ports; MCP exposure of declared
8282
prompts/tools is the one remaining roadmap item:
8383

8484
1. **Codegen** — emit idiomatic per-language code (Drizzle/Zod + Fastify for TS,
85-
POJO + OMDB for Java, `@Serializable data class` + Exposed for Kotlin, EF Core
85+
POJO + OMDB for Java, `data class` + Exposed for Kotlin, EF Core
8686
record + ASP.NET routes for C#, `@dataclass` for Python). Hand-edit-preserving
8787
regen via three-way merge.
8888
2. **Runtime metadata** — load metadata at runtime, drive behavior dynamically

agent-context/skills/metaobjects-audit/references/kotlin.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22

33
The Kotlin port is a **codegen tier built on top of the Java port** — the loader,
44
render engine, and Maven plugin are all Java; `metaobjects-codegen-kotlin` emits
5-
idiomatic Kotlin (KotlinPoet: `@Serializable data class`, Exposed `Table` objects,
5+
idiomatic Kotlin (KotlinPoet: `data class` (no `@Serializable` — entities are
6+
Jackson-compatible, not kotlinx-serialized), Exposed `Table` objects,
67
Spring `@RestController`). Codegen runs as the same Maven plugin goal the Java port
78
uses (`mvn metaobjects:generate`). Schema migration and live-DB drift are
89
**Node-`meta`-only** (ADR-0015). The runtime persistence tier is **JetBrains Exposed**

agent-context/skills/metaobjects-codegen/references/kotlin.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22

33
The Kotlin port is a **codegen tier built on top of the Java port**. The loader,
44
render engine, and Maven plugin are all Java; `codegen-kotlin` emits idiomatic
5-
Kotlin (`@Serializable data class`, Exposed `Table` objects, Spring
5+
Kotlin (`data class` (no `@Serializable` — entities are Jackson-compatible, not
6+
kotlinx-serialized), Exposed `Table` objects, Spring
67
`@RestController`/`@Configuration`) via KotlinPoet. Codegen runs as the same
78
build-time Maven plugin goal the Java port uses — there is no standalone `meta`
89
binary on the JVM side (the Node `meta` is for schema migrations only; see the
@@ -94,7 +95,7 @@ All live in `metaobjects-codegen-kotlin` under
9495

9596
| Generator | Output |
9697
|---|---|
97-
| `KotlinEntityGenerator` | `<Entity>.kt``@Serializable data class` per `object.entity` / `object.value`. A TPH `@discriminator` base's data class is the **union** of every subtype's columns (each folded nullable, validation dropped) so one wire shape backs the polymorphic + per-subtype endpoints. |
98+
| `KotlinEntityGenerator` | `<Entity>.kt``data class` (no `@Serializable`; Jackson-compatible) per `object.entity` / `object.value`. A TPH `@discriminator` base's data class is the **union** of every subtype's columns (each folded nullable, validation dropped) so one wire shape backs the polymorphic + per-subtype endpoints. |
9899
| `KotlinExposedTableGenerator` | `<Entity>Table.kt` — Exposed `Table` object (PK + FK + `@storage` columns) for entities with `source.rdb`. A TPH `@discriminator` base emits ONE `Table` for the whole hierarchy — every subtype-only column folded in `.nullable()` (a row of another subtype stores null there) — single-table inheritance; subtype entities emit no table of their own. |
99100
| `KotlinRelationsGenerator` | `<Entity>Relations.kt` — extension fns for `@cardinality="many"` query helpers |
100101
| `KotlinSpringControllerGenerator` | `<Entity>Controller.kt` — Spring `@RestController`, five CRUD endpoints on the cross-port REST contract, for writable entities (`source.rdb` `@kind="table"`). A TPH `@discriminator` base emits ONE controller: polymorphic `GET /<base>(+/{id})` plus a per-subtype CRUD set at `/<base>/<discriminatorValue lowercased>` — create injects the discriminator from the URL (never the body); get/update/delete are scoped to the subtype (cross-subtype → 404); the discriminator is immutable. |

agent-context/skills/metaobjects-runtime-ui/references/kotlin.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ The Kotlin port runtime is **generated Exposed `Table` objects plus your own
44
JetBrains Exposed transactions** — there is no Kotlin-specific persistence engine.
55
`KotlinExposedTableGenerator` emits one `<Entity>Table.kt` per entity with a
66
`source.rdb`; you hand-write the (trivial) transaction bodies, since the table
7-
column definitions and the `@Serializable` entity data class are both generated
7+
column definitions and the generated `data class` entity are both generated
88
from the same metadata.
99

1010
(If you want a fully metadata-driven engine instead of hand-written Exposed, the
@@ -20,7 +20,6 @@ emit a data class and an Exposed `Table`:
2020

2121
```kotlin
2222
// generated/acme/blog/Author.kt
23-
@Serializable
2423
data class Author(
2524
val id: Long,
2625
val name: String,

docs/features/entities.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -169,16 +169,15 @@ public class Author {
169169

170170
### Kotlin
171171

172-
`metaobjects-codegen-kotlin` (`KotlinEntityGenerator`) emits a `@Serializable
173-
data class` and (with `KotlinExposedTableGenerator`) an Exposed `Table` object.
172+
`metaobjects-codegen-kotlin` (`KotlinEntityGenerator`) emits a plain
173+
`data class` (no `@Serializable` — entities are Jackson-compatible, not
174+
kotlinx-serialized) and (with `KotlinExposedTableGenerator`) an Exposed
175+
`Table` object.
174176

175177
```kotlin
176178
// generated/acme/blog/Author.kt
177179
package acme.blog
178180
179-
import kotlinx.serialization.Serializable
180-
181-
@Serializable
182181
data class Author(
183182
val id: Long,
184183
val name: String,

docs/features/field-types.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,6 @@ public enum Status { DRAFT, PUBLISHED, ARCHIVED }
141141

142142
```kotlin
143143
// generated/acme/blog/Author.kt
144-
@Serializable
145144
data class Author(
146145
val id: Long,
147146
val name: String,

docs/ports/csharp.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,8 @@ The codegen emits:
9494

9595
- `Author.cs` — record per entity.
9696
- `AppDbContext.cs``DbSet<Author>`, projection `.ToView()`, `@storage` owned
97-
types via `OwnsOne`, enum-as-string via `HasConversion<string>()`.
97+
types via `OwnsOne` (single) / `OwnsMany(...).ToJson(...)` (`@isArray` array-of-VO),
98+
enum-as-string via `HasConversion<string>()`.
9899
- `Author.routes.cs` — CRUD minimal-API endpoints.
99100

100101
## Use
@@ -270,7 +271,7 @@ are not yet generated — see
270271
| Entities + fields | Yes |
271272
| Relationships + FK | Yes (EF Core + Postgres FK clause) |
272273
| Source kinds (table / view / storedProc) | `table` + `view` fully shipped; `storedProc` / `tableFunction` / `materializedView` partial |
273-
| `field.currency` / `field.enum` / `field.object` + `@storage` | Yes (incl. EF Core `OwnsOne` for `flattened`) |
274+
| `field.currency` / `field.enum` / `field.object` + `@storage` | Yes (incl. EF Core `OwnsOne` for `flattened`; `OwnsMany(...).ToJson(...)` for `@isArray` array-of-VO jsonb) |
274275
| Templates + render (FR-004) | Yes (`MetaObjects.Render`) |
275276
| Output parser codegen (FR-006) | Yes (`OutputParserGenerator``Parse`/`TryParse` BCL pattern) |
276277
| Payload-VO codegen | Yes (`MetaObjects.Codegen`) |

docs/ports/kotlin.md

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
Idiomatic Kotlin codegen target for Spring-Boot-Kotlin consumers on Exposed +
44
Flyway. The Kotlin port is a **codegen tier built on top of the Java port** — the
55
loader, OMDB persistence engine, render engine, Maven plugin, and conformance
6-
runners are all Java; Kotlin emits idiomatic Kotlin (`@Serializable data class`,
6+
runners are all Java; Kotlin emits idiomatic Kotlin (`data class`,
77
Exposed `Table` objects, extension-fn relationship helpers, Spring `@Configuration`
88
wiring) via KotlinPoet.
99

@@ -39,6 +39,26 @@ Two modules:
3939
<artifactId>exposed-core</artifactId>
4040
<version>${exposed.version}</version>
4141
</dependency>
42+
<!-- Generated typed `field.object @storage:jsonb` / `field.map` columns serialize through a
43+
generated per-package `MetaJsonbMapper.kt` Jackson `ObjectMapper` (no kotlinx-serialization
44+
compiler plugin required). -->
45+
<dependency>
46+
<groupId>com.fasterxml.jackson.core</groupId>
47+
<artifactId>jackson-databind</artifactId>
48+
<version>${jackson.version}</version>
49+
</dependency>
50+
<dependency>
51+
<groupId>com.fasterxml.jackson.module</groupId>
52+
<artifactId>jackson-module-kotlin</artifactId>
53+
<version>${jackson.version}</version>
54+
</dependency>
55+
<dependency>
56+
<groupId>com.fasterxml.jackson.datatype</groupId>
57+
<artifactId>jackson-datatype-jsr310</artifactId>
58+
<version>${jackson.version}</version>
59+
</dependency>
60+
<!-- FR-006 output parser + prompt-payload lane; also backs the open-bag
61+
`field.string @dbColumnType:jsonb` → kotlinx `JsonElement` path. -->
4262
<dependency>
4363
<groupId>org.jetbrains.kotlinx</groupId>
4464
<artifactId>kotlinx-serialization-json</artifactId>
@@ -53,7 +73,7 @@ The 9 generators in `codegen-kotlin`:
5373

5474
| Generator | Output | Per |
5575
|---|---|---|
56-
| `KotlinEntityGenerator` | `<Entity>.kt``@Serializable data class` | every `object.entity` + `object.value` |
76+
| `KotlinEntityGenerator` | `<Entity>.kt`Kotlin `data class` (Jackson-compatible; no `@Serializable`) | every `object.entity` + `object.value` |
5777
| `KotlinExposedTableGenerator` | `<Entity>Table.kt` — Exposed `Table` object with PK + FK + `@storage` columns | entities with `source.rdb` |
5878
| `KotlinRelationsGenerator` | `<Entity>Relations.kt` — extension fns for `cardinality=many` query helpers | entities with to-many relationships |
5979
| `KotlinPayloadGenerator` | `<Template>Payload.kt``@Serializable` payload from `@payloadRef` view-object | every `template.prompt` / `template.output` |
@@ -139,7 +159,6 @@ emits:
139159

140160
```kotlin
141161
// generated/acme/blog/Author.kt
142-
@Serializable
143162
data class Author(
144163
val id: Long,
145164
val name: String,

0 commit comments

Comments
 (0)