Commit dc72201
TML-2852: enums become first-class in application code — typed I/O, db.enums, declaration-order ORDER BY (#769)
## At a glance
In the demo's own contract, `Post` carries a `Priority` enum — and the
column is typed as its value union everywhere you touch it:
```ts
const Priority = enumType('Priority', pgText,
member('Low', 'low'), member('High', 'high'), member('Urgent', 'urgent')); // declaration order ≠ lexical
// on a real model: Post { …, priority: field.namedType(Priority) }
// READ — priority is 'low' | 'high' | 'urgent' (not string), sorted by DECLARATION order
getPostsByPriority() // → rows ordered low, low, high, urgent (not lexical)
// WRITE — only member values compile; 'nope' is a compile error, and the CHECK constraint rejects it at the DB
// INTROSPECT at runtime
db.enums.public.Priority.values // ['low', 'high', 'urgent'] (ordered, literal-typed)
db.enums.public.Priority.members.High // 'high'
```
This runs end-to-end against PGlite in `examples/prisma-next-demo` —
typed read, `db.enums.<ns>`, declaration-order `ORDER BY`, and the
slice-2 `CHECK` rejecting out-of-union writes.
## What this decides
An enum becomes a **first-class application concept**: its value union
flows into the static read/write types of **both** query lanes,
`db.enums.<ns>.<Name>` exposes it at runtime, and `ORDER BY` on an enum
column sorts by **declaration order**. It works for **text and
int-backed** enums and in **both authoring forms** (definition *and*
factory `defineContract`). Built on the merged substrate (slice 1) and
check-constraint enforcement (slice 2), and lands **additively** — PSL
`enum` stays native until the cutover, so only `enumType`-authored
contracts exercise it.
## How it builds up
1. **Typed I/O (R4/R5)** — narrow the codec type by the field's
`valueSet` to the value union, on **both paths**: the authored
`Definition` (no-emit) and the **emitted `contract.d.ts`** — the emitter
resolves a field's `valueSet` ref to the enum's member-value union,
codec-agnostically (text and int). Both lanes inherit it through the
field-output typemap; **non-enum fields are unchanged**.
2. **`db.enums.<ns>.<Name>` (R6)** — a runtime, literal-typed accessor
(`.values` / `.members.X` / `.has` / `.nameOf` / `.ordinalOf`) built
from that namespace's domain enums. Enums are **lane-agnostic contract
metadata**, so `db.enums` lives on the **`db` facade** alongside
`transaction` / `prepare` / `raw` / `context` (decided with the query
team) — a namespace-keyed map projected per target exactly like `db.sql`
/ `db.orm`. It matches the IR (`domain.namespaces[ns].enum`) and lets
the same enum name in two namespaces resolve independently.
Unbound-namespace targets (sqlite/mongo) get `db.enums.<Name>` via the
existing per-facade projection. Because enums sit on the facade rather
than adjacent to models, no reserved-name guard is needed — a model
named `enums` no longer collides.
3. **Declaration-order `ORDER BY` (R8, Postgres)** — renders
`array_position(ARRAY[…]::text[], col)` over the value-set's ordered
values.
4. **Factory-form authoring** — the new enum is authorable in the demo's
*real* factory-form contract (a top-level `enums` key threaded through
the factory overload, mirroring the definition form), not only the
definition form.
## Scope
**Additive / dark.** PSL `enum` keeps lowering to native (the repoint is
the cutover, TML-2853); member defaults are TML-2855; non-Postgres
`ORDER BY` (MySQL `FIELD(...)` / SQLite `CASE`) is future. Existing
fixtures are byte-identical apart from the demo.
## CI note
The repo-wide `pnpm typecheck` is red on **inherited `Cannot find
module` subpath errors** (`/contract-builder`, `/migration`, `/adapter`,
`/aggregate`, `/constants`) that reproduce on clean `origin/main` — a
separate main-health issue, not introduced here.
## Alternatives considered
- **Narrow from the emitted contract JSON** rather than the authored
`Definition` — rejected: emission widens the value-set to `string[]`,
erasing the literals.
- **A bespoke "enum codec"** — rejected: every field already carries a
codec; the enum is a `valueSet` restriction layered on top.
- **A dedicated `orderByDeclarationOrder()` API** — rejected: ordering
an enum column by declaration order should just work; the renderer
handles it implicitly.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
## Release Notes
* **New Features**
* Added enum type definitions with first-class support for enum-backed
model fields in SQL contracts.
* Enum fields now narrow to literal value unions (e.g., `'user' |
'admin'` instead of generic `string`) in both read and write operations,
with compile-time type checking.
* Exposed enum metadata and lookup utilities (values list, names, member
maps, membership/ordinal checks) on the client facade under
`db.enums.<namespace>.<EnumName>` (lane-agnostic namespace-keyed map).
* Enum-backed ORDER BY now respects enum declaration order rather than
lexical order.
* Added support for both string and numeric enum values.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Signed-off-by: Will Madden <madden@prisma.io>
Co-authored-by: Will Madden <madden@prisma.io>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>1 parent 890d102 commit dc72201
58 files changed
Lines changed: 3454 additions & 211 deletions
File tree
- examples/prisma-next-demo
- prisma
- src/prisma-no-emit
- test
- packages
- 1-framework
- 0-foundation/contract
- src
- exports
- test
- 3-tooling/emitter
- src
- test
- 2-sql
- 1-core/contract/src
- ir
- 2-authoring/contract-ts
- src
- exports
- test
- 4-lanes
- query-builder
- src
- test
- sql-builder/test
- 9-family/src/core/migrations
- 3-extensions
- postgres
- src
- contract
- exports
- runtime
- test
- contract-builder
- sql-orm-client
- src
- test
- sqlite/src/runtime
- 3-targets
- 3-targets/postgres/src/core
- 6-adapters/postgres
- src/core
- test/migrations
- projects/enums-as-domain-concept
- slices/application-read-surface
- skills
- extension-author/prisma-next-extension-upgrade/upgrades/0.13-to-0.14
- upgrade/prisma-next-upgrade/upgrades/0.13-to-0.14
- test
- integration/test
- utils/src
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | | - | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
3 | 13 | | |
4 | 14 | | |
5 | 15 | | |
| |||
27 | 37 | | |
28 | 38 | | |
29 | 39 | | |
| 40 | + | |
30 | 41 | | |
31 | 42 | | |
32 | 43 | | |
33 | 44 | | |
34 | 45 | | |
35 | 46 | | |
36 | 47 | | |
| 48 | + | |
37 | 49 | | |
38 | 50 | | |
39 | 51 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | | - | |
2 | | - | |
3 | 1 | | |
4 | | - | |
5 | | - | |
| 2 | + | |
6 | 3 | | |
7 | 4 | | |
8 | | - | |
9 | | - | |
10 | 5 | | |
11 | 6 | | |
12 | 7 | | |
13 | | - | |
14 | | - | |
15 | | - | |
16 | | - | |
17 | | - | |
18 | | - | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
19 | 12 | | |
20 | | - | |
21 | | - | |
22 | | - | |
23 | | - | |
24 | | - | |
25 | | - | |
26 | | - | |
27 | | - | |
28 | | - | |
29 | | - | |
30 | | - | |
31 | | - | |
32 | | - | |
33 | | - | |
34 | | - | |
35 | | - | |
36 | | - | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
37 | 17 | | |
38 | 18 | | |
39 | | - | |
40 | | - | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
41 | 22 | | |
42 | 23 | | |
43 | 24 | | |
| |||
Lines changed: 30 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
Lines changed: 151 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
Lines changed: 29 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
44 | 44 | | |
45 | 45 | | |
46 | 46 | | |
| 47 | + | |
47 | 48 | | |
48 | 49 | | |
49 | 50 | | |
| |||
Lines changed: 2 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
| 2 | + | |
2 | 3 | | |
3 | 4 | | |
4 | 5 | | |
| |||
34 | 35 | | |
35 | 36 | | |
36 | 37 | | |
37 | | - | |
| 38 | + | |
38 | 39 | | |
39 | 40 | | |
40 | 41 | | |
| |||
0 commit comments