Summary
When a projection (view) field is bound to a base-entity field via a dotted extends, the @metaobjectsdev/codegen-ts port does not carry the base field's isArray / storage: jsonb through the extends resolution — but the Kotlin port does. So for identical metadata, the two ports emit different types for the same projection field, and the TS output is both wrong and internally inconsistent.
Repro (minimal)
Base entity with an array column and a jsonb column:
object.pojo:
name: SomeEntity
children:
- field.string: { name: conditions, isArray: true } # → text[]
- field.object: { name: profile, objectRef: Profile, storage: jsonb } # → jsonb
A projection binds them via bare extends (no restated shape — this is intentional: keeping the projection a pure extends passthrough is what keeps it meta migrate-ready):
object.projection:
name: SomeEntityView
children:
- field.string: { name: conditions, extends: "…::SomeEntity.conditions" }
- field.object: { name: profile, extends: "…::SomeEntity.profile" }
Kotlin codegen — correct (resolves extends and carries the shape):
val conditions: List<String>
val profile: JsonElement
TS codegen (codegen-ts 0.15.21) — wrong: both the Drizzle column and the Zod schema flatten the array to a scalar string, and they even disagree with each other on the jsonb column:
// drizzle table
conditions: text("conditions"), // should be text("conditions").array()
profile: jsonb("profile"), // drizzle gets jsonb right…
// zod schema
conditions: z.string(), // should be z.array(z.string())
profile: z.string(), // …but zod says string — internally inconsistent with the drizzle jsonb() above
Impact
The projection's generated TS type is a type-lie: a text[] column is typed string, and a jsonb column is typed string even though the same generated file's Drizzle side declares jsonb(). Any consumer that trusts the generated type — or .parse()s a real row through the generated Zod schema — gets the wrong type or a runtime validation failure, because the actual wire shape (defined by the Kotlin-generated projection that backs the REST/read API) is an array / object.
This is a lagging-port divergence: the Kotlin port carries isArray / storage through extends, the TS port drops it. The ports should agree on the resolved field shape (cf. "keep all ports in sync").
Suggested fix
In codegen-ts, when resolving a projection field's extends, inherit the referenced base field's isArray and storage / dbColumnType (jsonb) the same way the Kotlin port does — so the Drizzle column, the Zod schema, and the Kotlin type all agree. Fixing it in the generator (rather than forcing adopters to re-state isArray/storage on every projection field) also preserves the pure-extends passthrough shape that keeps projections meta migrate-ready.
Summary
When a projection (view) field is bound to a base-entity field via a dotted
extends, the@metaobjectsdev/codegen-tsport does not carry the base field'sisArray/storage: jsonbthrough theextendsresolution — but the Kotlin port does. So for identical metadata, the two ports emit different types for the same projection field, and the TS output is both wrong and internally inconsistent.Repro (minimal)
Base entity with an array column and a jsonb column:
A projection binds them via bare
extends(no restated shape — this is intentional: keeping the projection a pureextendspassthrough is what keeps itmeta migrate-ready):Kotlin codegen — correct (resolves
extendsand carries the shape):TS codegen (
codegen-ts0.15.21) — wrong: both the Drizzle column and the Zod schema flatten the array to a scalar string, and they even disagree with each other on the jsonb column:Impact
The projection's generated TS type is a type-lie: a
text[]column is typedstring, and ajsonbcolumn is typedstringeven though the same generated file's Drizzle side declaresjsonb(). Any consumer that trusts the generated type — or.parse()s a real row through the generated Zod schema — gets the wrong type or a runtime validation failure, because the actual wire shape (defined by the Kotlin-generated projection that backs the REST/read API) is an array / object.This is a lagging-port divergence: the Kotlin port carries
isArray/storagethroughextends, the TS port drops it. The ports should agree on the resolved field shape (cf. "keep all ports in sync").Suggested fix
In
codegen-ts, when resolving a projection field'sextends, inherit the referenced base field'sisArrayandstorage/dbColumnType(jsonb) the same way the Kotlin port does — so the Drizzle column, the Zod schema, and the Kotlin type all agree. Fixing it in the generator (rather than forcing adopters to re-stateisArray/storageon every projection field) also preserves the pure-extendspassthrough shape that keeps projectionsmeta migrate-ready.