v0.16.0
This release makes contract infer output round-trip cleanly through contract emit, materializes foreign keys and indexes as discrete contract entities, fixes the first-run experience of the Supabase extension end to end, and adds per-codec temporal presets that spell column type and auto-update behavior together.
Breaking changes
-
Foreign keys and indexes are discrete contract entities —
contract emitnow materializes each foreign key'sconstraint/indexauthoring booleans into separate persisted entities: aforeignKeys[]entry is the referential constraint only, and every backing index (including one backing a foreign key) is its own namedindexes[]entry. The authoring surface is unchanged (@relation(index:), TSfk({ constraint, index }),foreignKeyDefaults), and re-runningcontract emitregenerates the new shape with no source change. TypeScript that read.constraint/.indexoff a contract'sforeignKeys[]entry must read the discreteindexes[]entry instead. No migration or DDL change — the schema the planner anddb verifyderive is identical. See the 0.15-to-0.16 upgrade recipe and the extension-author recipe. (#989)Before:
After:
"foreignKeys": [ { "source": { "columns": ["user_id"] }, ... } ], "indexes": [ { "columns": ["user_id"], "name": "identities_user_id_idx" } ]
-
A singular back-relation over a non-unique foreign key is rejected at emit — a schema declaring a 1:1 relation whose foreign-key columns are not covered by a unique constraint previously emitted a contract claiming a guarantee the database cannot enforce. Emit now fails with
PSL_NON_UNIQUE_BACKRELATION; add@unique/@@uniqueto the foreign-key fields, or make the back-relation field a list. (#1015)Before (accepted, emitted
cardinality: '1:1'):model Profile { id Int @id userId Int // no @unique user User @relation(fields: [userId], references: [id]) } model User { id Int @id profile Profile? }
After: the same schema fails emit with
PSL_NON_UNIQUE_BACKRELATION. Add@uniquetouserId(or makeprofileaProfile[]list). -
contract inferdeclares identity-column defaults, anddb verify --strictnow sees them — infer emits@default(autoincrement())for a PostgresGENERATED ... AS IDENTITYcolumn (previously nothing), anddb verifyintrospecting a live identity column resolves its default toautoincrement()too. If you rundb verify --strictagainst a table with an identity column whose contract predates this fix, verify reports that default as an unexpected extra — re-runcontract inferfor the affected table, or add@default(autoincrement())by hand. Without--strict, nothing changes. (#1011) -
contract inferback-relation names no longer double-pluralize — the hand-rolled pluralization rule turned already-plural table names intosessionses; infer now uses real inflection (sessionsstayssessions,statusstill becomesstatuses). Already-generated.prismafiles are untouched, but the nextcontract inferrun against a database with already-plural table names renames the affected back-relation fields — public field names your code reaches via.include()/.select()and the generated types — so diff the regenerated file and update call sites. (#1011) -
@prisma-next/extension-supabaseno longer exports./test/utils— thebootstrapSupabaseShimsubpath typechecked but never worked from npm (it reads fixture files that were never published, so every call failed with ENOENT). Delete the import and any test setup that called it. (#997)
Features
-
Per-codec temporal presets carry execution-default behavior as arguments, so a column's exact type and its auto-update behavior can finally be spelled together — e.g. the
timestamp(3)columns Prisma ORM migrations generate for@updatedAt.temporal.updatedAt()survives as shorthand fortemporal.timestamptz(onCreate: now, onUpdate: now). (#1003)model Page { updatedAt temporal.timestamp(3, onCreate: now, onUpdate: now) lastSeen temporal.timestamp(3) touched temporal.timestamptz(onUpdate: now) }
-
The Postgres target registers its built-in index types (
btree,hash,gin,gist,spgist,brin), so@@index(..., type: "gin")— whichcontract inferalready printed — now emits instead of throwingunregistered index type. (#1011)
Fixes
- Using
@prisma-next/extension-supabaseagainst a stock Supabase project now works first-try: generated migrations for RLS contracts import, typecheck, and run (RLS operations render as methods on the migration base class);migrate's remediation for a missing extension-space migration works when followed verbatim;db verifyno longer reports phantom missing constraints;db.asUser(jwt)supports the ES256/JWKS signing current Supabase uses; anddb.asServiceRole()queries succeed with the grants a real project provides. (#997) - More
contract inferround-trip fixes: a plainDecimalfield on Postgres no longer throwsCODEC_PARAMETERIZATION_MISMATCHat connect, the 1:1 back-relation shape infer prints is accepted by emit, literal-shapeddbgenerated(...)defaults compare equal indb verifyinstead of reporting permanent drift, and foreign keys pointing outside the introspected scope are explained in infer's output instead of vanishing silently. (#1011) - MTI variant-narrowed
updateCount,deleteCount, and include-backeddeleteAllnow compile their predicates through a correlated subquery that joins the variant table — previously the generated SQL could reference the variant table without it being in scope. (#940) - An explicit
.select(...)on a polymorphic query or include now restricts MTI variant fields to the selection; unselected variant fields no longer leak into results. Omitting the selection keeps the full default shape. (#984) - The language server surfaces config-load failures as a diagnostic on the config file (
PRISMA_NEXT_CONFIG_LOAD_FAILED) and keeps serving schema diagnostics from the last working configuration when a reload fails, instead of silently wiping every marker. (#974) - Migration operations are now ordered by a dependency graph over schema-diff issues instead of a hand-maintained per-kind integer table, fixing drop-order defects (e.g. a column dropping before its own constraint). For code reading the migration-diff internals:
SchemaDiffIssue.reasonis removed — discriminate via the presence ofexpected/actual, or theissueOutcomehelper from@prisma-next/framework-components/control. (#992)