@metaobjectsdev/sdk 0.15.12 → 0.15.14-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agent-context/skills/metaobjects-authoring/SKILL.md +38 -0
- package/agent-context/skills/metaobjects-codegen/references/java.md +19 -3
- package/agent-context/skills/metaobjects-codegen/references/kotlin.md +19 -3
- package/agent-context/skills/metaobjects-codegen/references/python.md +18 -2
- package/agent-context/skills/metaobjects-codegen/references/typescript.md +20 -2
- package/package.json +2 -2
|
@@ -580,6 +580,44 @@ across files (same `package` + same `name` → merged; last-writer-wins on attr
|
|
|
580
580
|
conflicts, structural children accumulate). Use `extends` to share shape between
|
|
581
581
|
distinct entities; use `overlay` to split one entity's declaration across files.
|
|
582
582
|
|
|
583
|
+
## Discriminator inheritance (TPH)
|
|
584
|
+
|
|
585
|
+
When several concrete entities are variants of one thing and should share a
|
|
586
|
+
**single table** (table-per-hierarchy / single-table inheritance), model it with a
|
|
587
|
+
**discriminator** rather than one table per variant:
|
|
588
|
+
|
|
589
|
+
- The **base** `object.entity` declares `@discriminator` naming a discriminator
|
|
590
|
+
field — typically a `field.enum` whose `@values` are the subtype tags.
|
|
591
|
+
- Each concrete **subtype** `extends` the base and declares `@discriminatorValue`
|
|
592
|
+
(one of those enum members).
|
|
593
|
+
|
|
594
|
+
All subtypes persist to the base's single table (subtype-only columns fold in
|
|
595
|
+
nullable). You author only the metadata; codegen emits the polymorphic surface —
|
|
596
|
+
per-subtype routes at `/<base>/<discriminatorValue lowercased>` where create
|
|
597
|
+
**injects** the discriminator from the URL, reads/updates/deletes are **scoped** to
|
|
598
|
+
the subtype (cross-subtype → 404), and the discriminator is **immutable**.
|
|
599
|
+
Supported + conformance-gated in all five ports (the repo's
|
|
600
|
+
`docs/features/abstracts-and-inheritance.md` has the full example and per-port
|
|
601
|
+
mapping).
|
|
602
|
+
|
|
603
|
+
```yaml
|
|
604
|
+
- object.entity:
|
|
605
|
+
name: Auth # TPH base — owns the single `auths` table
|
|
606
|
+
discriminator: type
|
|
607
|
+
children:
|
|
608
|
+
- source.rdb: { table: auths }
|
|
609
|
+
- field.long: { name: id }
|
|
610
|
+
- field.enum: { name: type, values: ["Bridge", "Copay"] }
|
|
611
|
+
- identity.primary: { fields: id }
|
|
612
|
+
|
|
613
|
+
- object.entity:
|
|
614
|
+
name: BridgeAuth # subtype — folded into `auths`, tagged type="Bridge"
|
|
615
|
+
extends: Auth
|
|
616
|
+
discriminatorValue: Bridge
|
|
617
|
+
children:
|
|
618
|
+
- field.int: { name: quantity, required: true }
|
|
619
|
+
```
|
|
620
|
+
|
|
583
621
|
---
|
|
584
622
|
|
|
585
623
|
For non-trivial schema design, use `/superpowers:brainstorming` if installed;
|
|
@@ -82,9 +82,9 @@ first group together:
|
|
|
82
82
|
|
|
83
83
|
| Generator | Output |
|
|
84
84
|
|---|---|
|
|
85
|
-
| `SpringControllerGenerator` | `<Entity>Controller.java` per writable entity (`source.rdb` `@kind="table"`) — Spring Web MVC, five CRUD endpoints on the cross-port REST contract (`?sort`, `?limit`/`?offset`, `?withCount=1` envelope, 404/400 envelopes) |
|
|
86
|
-
| `SpringDtoGenerator` | `<Entity>Dto.java` as a Java 21 `record`; wrapped primitives (`Long`/`Integer`/`Boolean`) so missing JSON props deserialise to `null`; currency = `Long` (integer minor units) |
|
|
87
|
-
| `SpringRepositoryGenerator` | `<Entity>Repository.java` — a hand-stubbed `interface` the consumer implements with their persistence layer (Spring Data JPA / jOOQ / JDBC) |
|
|
85
|
+
| `SpringControllerGenerator` | `<Entity>Controller.java` per writable entity (`source.rdb` `@kind="table"`) — Spring Web MVC, five CRUD endpoints on the cross-port REST contract (`?sort`, `?limit`/`?offset`, `?withCount=1` envelope, 404/400 envelopes). 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 scoped to the subtype (cross-subtype → 404); discriminator immutable. |
|
|
86
|
+
| `SpringDtoGenerator` | `<Entity>Dto.java` as a Java 21 `record`; wrapped primitives (`Long`/`Integer`/`Boolean`) so missing JSON props deserialise to `null`; currency = `Long` (integer minor units). A TPH `@discriminator` base's DTO is the **union** of every subtype's columns (subtype-only fields folded nullable, validation dropped), so one wire shape backs the polymorphic + per-subtype endpoints. |
|
|
87
|
+
| `SpringRepositoryGenerator` | `<Entity>Repository.java` — a hand-stubbed `interface` the consumer implements with their persistence layer (Spring Data JPA / jOOQ / JDBC). For a TPH base the interface is polymorphic + per-subtype-scoped (`listByType`/`findByIdAndType`/`createWithType`/`updateByIdAndType`/`deleteByIdAndType`) over the single table; subtype entities emit no own controller/DTO/repository — they fold into the base. |
|
|
88
88
|
| `SpringPayloadGenerator` | a Java 21 `record` per template payload VO |
|
|
89
89
|
| `SpringOutputParserGenerator` | the `template.output` parser-on-receipt (see the prompts reference) |
|
|
90
90
|
| `SpringFilterAllowlistGenerator` | per-entity filter allowlist |
|
|
@@ -92,3 +92,19 @@ first group together:
|
|
|
92
92
|
Metadata lives under `src/main/metaobjects/` in the same canonical JSON the other
|
|
93
93
|
ports read — fused-key form, `source.rdb` + `@table`, `@column` for a renamed
|
|
94
94
|
physical column.
|
|
95
|
+
|
|
96
|
+
## Discriminator inheritance (TPH)
|
|
97
|
+
|
|
98
|
+
`codegen-spring` fully supports **table-per-hierarchy (TPH) inheritance**. `TphPlan`
|
|
99
|
+
is the shared descriptor every TPH-aware generator reads: an `object.entity`
|
|
100
|
+
carrying `@discriminator` (naming a `field.enum`) is the base; concrete entities
|
|
101
|
+
that `extends` it and declare `@discriminatorValue` are its subtypes, all persisted
|
|
102
|
+
to the base's **single** table (single-table inheritance). `SpringDtoGenerator`
|
|
103
|
+
emits the base DTO as the union of subtype columns (each folded nullable);
|
|
104
|
+
`SpringControllerGenerator` mounts polymorphic reads + per-subtype CRUD scoped by
|
|
105
|
+
the discriminator (inject on create, subtype-scope + cross-subtype 404 on
|
|
106
|
+
get/update/delete, immutable discriminator); `SpringRepositoryGenerator` emits the
|
|
107
|
+
polymorphic + per-subtype-scoped repository seam the consumer implements against
|
|
108
|
+
Spring Data JPA / JDBC. Conformance-gated by `fixtures/api-contract-conformance/tph`
|
|
109
|
+
(HTTP wire shape) and `fixtures/persistence-conformance/tph-*` (single-table
|
|
110
|
+
runtime semantics).
|
|
@@ -94,10 +94,10 @@ All live in `metaobjects-codegen-kotlin` under
|
|
|
94
94
|
|
|
95
95
|
| Generator | Output |
|
|
96
96
|
|---|---|
|
|
97
|
-
| `KotlinEntityGenerator` | `<Entity>.kt` — `@Serializable data class` per `object.entity` / `object.value` |
|
|
98
|
-
| `KotlinExposedTableGenerator` | `<Entity>Table.kt` — Exposed `Table` object (PK + FK + `@storage` columns) for entities with `source.rdb` |
|
|
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
|
+
| `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. |
|
|
99
99
|
| `KotlinRelationsGenerator` | `<Entity>Relations.kt` — extension fns for `@cardinality="many"` query helpers |
|
|
100
|
-
| `KotlinSpringControllerGenerator` | `<Entity>Controller.kt` — Spring `@RestController`, five CRUD endpoints on the cross-port REST contract, for writable entities (`source.rdb` `@kind="table"`) |
|
|
100
|
+
| `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. |
|
|
101
101
|
| `KotlinPayloadGenerator` | `<Template>Payload.kt` — `@Serializable` payload data class from a template's `@payloadRef` |
|
|
102
102
|
| `KotlinOutputParserGenerator` | the `template.output` parser-on-receipt (see the prompts reference) |
|
|
103
103
|
| `KotlinValidatorGenerator` | `MetadataStartupValidator.kt` + `ExposedTableValidator.kt` (once per project) |
|
|
@@ -108,3 +108,19 @@ All live in `metaobjects-codegen-kotlin` under
|
|
|
108
108
|
Metadata lives under `src/main/metaobjects/` in the same canonical JSON the other
|
|
109
109
|
ports read — fused-key form, `source.rdb` + `@table`, `@column` for a renamed
|
|
110
110
|
physical column.
|
|
111
|
+
|
|
112
|
+
## Discriminator inheritance (TPH)
|
|
113
|
+
|
|
114
|
+
`codegen-kotlin` fully supports **table-per-hierarchy (TPH) inheritance**.
|
|
115
|
+
`KotlinTphPlan` is the shared descriptor every TPH-aware generator reads: an
|
|
116
|
+
`object.entity` carrying `@discriminator` (naming a `field.enum`) is the base;
|
|
117
|
+
concrete entities that `extends` it and declare `@discriminatorValue` are its
|
|
118
|
+
subtypes, all persisted to the base's **single** Exposed `Table` (single-table
|
|
119
|
+
inheritance). `KotlinExposedTableGenerator` folds each subtype's own columns into
|
|
120
|
+
that table as `.nullable()`; `KotlinEntityGenerator` builds the base data class as
|
|
121
|
+
the union of subtype columns; `KotlinFilterAllowlistGenerator` unions the
|
|
122
|
+
subtypes' filterable columns; `KotlinSpringControllerGenerator` mounts the
|
|
123
|
+
polymorphic reads + per-subtype CRUD scoped by the discriminator (inject on create,
|
|
124
|
+
subtype-scope + cross-subtype 404 on get/update/delete, immutable discriminator).
|
|
125
|
+
Conformance-gated by `fixtures/api-contract-conformance/tph` (HTTP wire shape) and
|
|
126
|
+
`fixtures/persistence-conformance/tph-*` (single-table runtime semantics).
|
|
@@ -37,12 +37,28 @@ a renamed physical column).
|
|
|
37
37
|
|
|
38
38
|
| Stable name | Output |
|
|
39
39
|
|---|---|
|
|
40
|
-
| `entity` | one **Pydantic model** per `object.entity` / projection (the `entity-model` generator): typed fields from the metadata, nullability from `@required`, `@maxLength`/validators, enum fields → a Python `Enum`. This is the typed data model. |
|
|
41
|
-
| `routes` | a **FastAPI `APIRouter`** per writable entity (`source.rdb @kind="table"`) on the cross-port REST contract (`?filter[field][op]=`, `?sort=field:asc`, `?limit`/`?offset`, `?withCount=1` envelope, 400/404 envelopes). The router declares a repository **`Protocol`** you implement and inject. |
|
|
40
|
+
| `entity` | one **Pydantic model** per `object.entity` / projection (the `entity-model` generator): typed fields from the metadata, nullability from `@required`, `@maxLength`/validators, enum fields → a Python `Enum`. This is the typed data model. A TPH concrete subtype (`@discriminatorValue`) pins the inherited `@discriminator` field to a `Literal[...]` so the model rejects a foreign-subtype tag. |
|
|
41
|
+
| `routes` | a **FastAPI `APIRouter`** per writable entity (`source.rdb @kind="table"`) on the cross-port REST contract (`?filter[field][op]=`, `?sort=field:asc`, `?limit`/`?offset`, `?withCount=1` envelope, 400/404 envelopes). The router declares a repository **`Protocol`** you implement and inject. A TPH `@discriminator` base emits ONE polymorphic router: `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 scoped to the subtype (cross-subtype → 404); discriminator immutable. Its repository `Protocol` is subtype-keyed (`subtype=None` for the polymorphic base) so your implementation applies the single-table discriminator scope. |
|
|
42
42
|
| `filter-allowlist` | per-entity filter allowlist (FR-009 — the server-side field+operator allowlist the routes validate against). |
|
|
43
43
|
| `payload` / `output-parser` / `output-prompt` / `extractor` / `render-helper` / `trace-helper` | the `template.output` prompt-pillar artifacts — see the **prompts** reference. |
|
|
44
44
|
| `template` | the generic Mustache `template` primitive. |
|
|
45
45
|
|
|
46
|
+
## Discriminator inheritance (TPH)
|
|
47
|
+
|
|
48
|
+
Python codegen fully supports **table-per-hierarchy (TPH) inheritance**
|
|
49
|
+
(`tph_plan.py` is the shared descriptor): an `object.entity` carrying
|
|
50
|
+
`@discriminator` (naming a `field.enum`) is the base; concrete entities that
|
|
51
|
+
`extends` it and declare `@discriminatorValue` are its subtypes, all persisted to
|
|
52
|
+
the base's **single** table (single-table inheritance). The `entity` generator
|
|
53
|
+
pins each subtype's inherited discriminator to a `Literal`; the `routes` generator
|
|
54
|
+
emits the polymorphic router + per-subtype CRUD scoped by the discriminator (inject
|
|
55
|
+
on create, subtype-scope + cross-subtype 404 on get/update/delete, immutable
|
|
56
|
+
discriminator). Because Python owns no ORM (see below), your repository — keyed by
|
|
57
|
+
subtype — applies the single-table discriminator scope (idiomatically a
|
|
58
|
+
SQLAlchemy polymorphic/single-table mapping). Conformance-gated by
|
|
59
|
+
`fixtures/api-contract-conformance/tph` (HTTP wire shape) and
|
|
60
|
+
`fixtures/persistence-conformance/tph-*` (single-table runtime semantics).
|
|
61
|
+
|
|
46
62
|
## No ORM — you own persistence (unlike the C# port)
|
|
47
63
|
|
|
48
64
|
Python codegen emits the **Pydantic models + the FastAPI routers**, but **no ORM /
|
|
@@ -69,13 +69,31 @@ From `@metaobjectsdev/codegen-ts/generators` (server-side, framework-neutral):
|
|
|
69
69
|
|
|
70
70
|
| Generator | Emits per entity |
|
|
71
71
|
|---|---|
|
|
72
|
-
| `entityFile()` | `<Entity>.ts` — Drizzle table + FK `.references()` + `relations()` + inferred types + Zod insert/update schemas + `<Entity>FilterAllowlist` / `<Entity>SortAllowlist` |
|
|
72
|
+
| `entityFile()` | `<Entity>.ts` — Drizzle table + FK `.references()` + `relations()` + inferred types + Zod insert/update schemas + `<Entity>FilterAllowlist` / `<Entity>SortAllowlist`. A TPH `@discriminator` base folds every subtype's columns into ONE Drizzle table (subtype-only columns nullable, no default — single-table inheritance) and emits a discriminated-union type + per-subtype Zod schemas + a `parse<Base>` dispatcher; subtype entities emit no table of their own. |
|
|
73
73
|
| `queriesFile()` | `<Entity>.queries.ts` — typed CRUD (`findPostById`, `listPosts`, `createPost`, `updatePost`, `deletePostById`) |
|
|
74
|
-
| `routesFile()` | `<Entity>.routes.ts` — Fastify CRUD routes on the cross-port REST contract. `routesFileHono()` is the Hono/Workers variant |
|
|
74
|
+
| `routesFile()` | `<Entity>.routes.ts` — Fastify CRUD routes on the cross-port REST contract. `routesFileHono()` is the Hono/Workers variant. A TPH `@discriminator` base mounts polymorphic `GET /<base>(+/:id)` plus a per-subtype CRUD set at `<basePath>/<discriminatorValue lowercased>` — create omits the discriminator (the URL names the subtype; the runtime injects it); get/update/delete scoped to the subtype (cross-subtype → 404); discriminator immutable via the runtime `discriminator` option. |
|
|
75
75
|
| `barrel()` | `index.ts` re-exporting each `<Entity>.ts` (one-shot, not per-entity) |
|
|
76
76
|
| `promptRender()` | `render<Name>()` per `template.prompt` |
|
|
77
77
|
| `outputParser()` | `<Name>.output.ts` (`parse*` / `safeParse*`) per `template.output` |
|
|
78
78
|
|
|
79
|
+
## Discriminator inheritance (TPH)
|
|
80
|
+
|
|
81
|
+
The TS reference implementation fully supports **table-per-hierarchy (TPH)
|
|
82
|
+
inheritance** (`tph-discriminator.ts` is the shared descriptor): an `object.entity`
|
|
83
|
+
carrying `@discriminator` (naming a `field.enum`) is the base; concrete entities
|
|
84
|
+
that `extends` it and declare `@discriminatorValue` are its subtypes, all persisted
|
|
85
|
+
to the base's **single** Drizzle table (single-table inheritance). `entityFile()`
|
|
86
|
+
folds each subtype's columns into that table nullable and emits the
|
|
87
|
+
discriminated-union type + per-subtype Zod schemas + a `parse<Base>` dispatcher;
|
|
88
|
+
`routesFile()` mounts polymorphic reads + per-subtype CRUD scoped by the
|
|
89
|
+
discriminator. At runtime, `@metaobjectsdev/runtime-ts`'s ObjectManager enforces the
|
|
90
|
+
subtype contract: it injects the discriminator on create, scopes every
|
|
91
|
+
read/update/delete to the subtype (a foreign-subtype row is invisible), and treats
|
|
92
|
+
the discriminator as immutable — mirroring the generated per-subtype route's
|
|
93
|
+
cross-subtype 404. Conformance-gated by `fixtures/api-contract-conformance/tph`
|
|
94
|
+
(HTTP wire shape) and `fixtures/persistence-conformance/tph-*` (single-table
|
|
95
|
+
runtime semantics).
|
|
96
|
+
|
|
79
97
|
## Docs — `meta docs` (one door, two surfaces)
|
|
80
98
|
|
|
81
99
|
Documentation is NOT a `meta gen` generator. The single door is the `meta docs`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@metaobjectsdev/sdk",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.14-rc.1",
|
|
4
4
|
"description": "Workspace helpers and agent-docs utilities for MetaObjects projects.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"access": "public"
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
|
-
"@metaobjectsdev/metadata": "0.15.
|
|
59
|
+
"@metaobjectsdev/metadata": "0.15.14-rc.1",
|
|
60
60
|
"zod": "^3.23.0"
|
|
61
61
|
},
|
|
62
62
|
"devDependencies": {
|