The rolling, newest-first index of Prisma Next releases. Each entry mirrors the release's committed notes file under docs/releases/ (the body of its GitHub Release) under a ## v<version> header — see docs/releases/README.md for the convention and authoring template.
Changelog tracking starts at v0.12.0, the first release cut after this convention landed. For v0.11.0 and earlier, see the GitHub Releases page — historical notes are not backfilled here.
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.
-
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)
-
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)
- 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)
This release ships Postgres row-level security end-to-end (policies for every operation, explicit @@rls enablement, role declarations — authored in PSL or TypeScript, planned by migration plan, drift caught by db verify), native Postgres enums (external adoption and a managed lifecycle), the complete introspected Supabase contract, a PSL language server (prisma-next lsp) with formatting, completions, and semantic highlighting, native scalar-list columns, PSL many-to-many authoring, and one unified schema differ behind db verify and migration planning. SQL ORM includes now decode through codecs, matching top-level reads.
-
SQL ORM includes decode through codecs — every scalar field of an included relation now decodes through its contract-bound codec, matching top-level query results. Code that relied on included fields keeping the database's raw JSON representation must be updated: Postgres
byteainclude fields returnUint8Arrayinstead of\x-prefixed hex text, timestamp fields returnDateinstead of strings, and custom codec-backed fields return whatever the codec'sdecodeJsonproduces. Custom SQL codec authors:encodeJson/decodeJsonnow use the exact scalar shape the database produces inside JSON values — see the extension-author recipe for the built-in representation changes. (#942)Before:
const [post] = await db.orm.public.Post.find({ include: { author: true } }); post.author.avatar; // '\\x89504e…' (raw hex text) post.author.createdAt; // '2026-07-01T12:00:00' (string)
After:
post.author.avatar; // Uint8Array post.author.createdAt; // Date
-
db verify --jsonreports a singleschema.issueslist — the splitschema.issues/schema.schemaDiffIssuespair collapses into oneschema.issuesarray of{ path, reason, message, expected?, actual? }, and the retiredoutcomefield is replaced byreason('missing'→'not-found','extra'→'not-expected','mismatch'→'not-equal'). The same collapse applies toschema.warnings. Update scripts or CI steps that readschemaDiffIssuesor compare.outcome. See the 0.14→0.15 upgrade recipe. (#921)Before:
{ "schema": { "issues": [], "schemaDiffIssues": [{ "outcome": "missing", "message": "…" }] } }After:
{ "schema": { "issues": [{ "reason": "not-found", "path": ["…"], "message": "…" }] } } -
RLS policies require
@@rlson the target model — RLS enablement is an explicit, authored table attribute. Apolicy_*block'stargetmodel must declare@@rls, orcontract emitfails withPSL_EXTENSION_TARGET_MODEL_MISSING_ATTRIBUTE. Plan semantics follow the marker: a marked table with RLS off plansENABLE ROW LEVEL SECURITY, removing every policy keeps RLS enabled (fail-closed deny-all), and removing@@rlsplansDISABLE ROW LEVEL SECURITY(requires the destructive allowance). Renaming only a policy's name plans a singleALTER POLICY … RENAME TOinstead of drop+create. Extension authors constructingPostgresTableSchemaNodeby hand must supply the now-requiredrlsEnabledboolean. (#945)Before:
model Profile { id Uuid @id userId Uuid @unique }
After:
model Profile { id Uuid @id userId Uuid @unique @@rls }
-
Extension authors: SQL contract authoring requires a target
createNamespace— the SQL family no longer materialises a placeholder namespace, soprismaContract(...)/defineContract(...)from@prisma-next/sql-contract-psl/@prisma-next/sql-contract-tsneed the target's namespace factory (postgresCreateNamespace/sqliteCreateNamespace); target-packdefineContractwrappers already supply it, so app authors are unaffected.SqlNamespaceis now an abstract class;buildSqlNamespace,buildSqlNamespaceMap,SqlBoundNamespace, andSqlUnboundNamespaceare removed, and hand-written namespace literals carry the targetkind(e.g.'postgres-schema') instead of'sql-namespace'. See the extension-author recipe. (#864) -
Extension authors: the coordinate-based schema-diff SPI is retired — the migration planner and
db verifynow run on one generic node differ.collectSqlSchemaIssues/collectSqlSchemaIssuesPerNamespace,diffPostgresDatabaseSchema, andSqlControlTargetDescriptor.diffDatabaseSchemaare removed (usediffSchemasor a target'sbuildXPlanDiff);MigrationPlanner.plan()'skeepDiffIssuepredicate is replaced by anownershiporacle; the issue typesBaseSchemaIssue/SchemaIssue/EnumValuesChangedIssueare gone —SchemaDiffIssueis the single issue shape everywhere, including the codecverifyTypehook; andgraphWalkStrategyis renamedresolveRecordedPathin@prisma-next/migration-tools/aggregate. See the extension-author recipe. (#921, #894) -
Extension authors: restricted-column typing goes through the codec — a column restricted to a value set derives its TS literal union by rendering each stored value through the codec's
renderValueLiteral(value, side), replacing the framework's deleted domain-enum override. Custom codec descriptors used by enum/restricted columns must implement it, or the column widens to the codec's output type. (#896) -
Extension authors: Mongo
deriveJsonSchemasources enums from value sets — the fourth argument ofderiveJsonSchema/derivePolymorphicJsonSchemachanges from a domain-enum map to a value-set map (contract.storage.namespaces[<ns>].entries.valueSet). Callers throughmongoContract(...)/defineContract(...)need no change. (#900) -
Extension authors:
ScalarFieldState's first generic is the column descriptor —ScalarFieldState<'pg/text@1', …>becomesScalarFieldState<ColumnTypeDescriptor<'pg/text@1'>, …>, so field states preserve the whole descriptor type (including native-enum member tuples). Built contract types also keep literalnativeType/typeParamsinstead of widening tostring. (#958) -
Extension authors:
native_enumentities serialize intocontract.json, keyed by physical type name — packs declaring native Postgres enums must re-emit their bundled contract so theentries.native_enummaps land in the published artifacts (this is what lets a consumer'scontract infersubtract pack-owned enum types). Code addressing an entry by key switches from the PascalCase name to the physical Postgres type name (entries.native_enum.aal_level, not.AalLevel). (#946, #954)
-
Postgres row-level security, end-to-end — PSL gains
policy_select,policy_insert,policy_update,policy_delete, andpolicy_allblocks (withusing/withCheckpredicates and per-role targeting), the@@rlsenablement attribute, and standaloneroledeclarations insidenamespace unbound { }.migration planplans the full lifecycle (ENABLE/DISABLE ROW LEVEL SECURITY, policy create/drop, rename viaALTER POLICY), anddb verifyfails on policy drift and on declared roles the live cluster lacks. The same surface is authorable in the TypeScript DSL (policySelect(...),rlsEnabled(model),role(name)), producing wire-name-identical contracts. (#771, #868, #945, #950, #957, #959) -
Native Postgres enums —
CREATE TYPE … AS ENUMtypes are first-class again, this time as explicit entities. External types the database already owns (e.g. Supabase'sauth.aal_level) are declared vianative_enumblocks, typed as member-value literal unions, adopted bycontract infer, and read at runtime through the new Postgres-onlydb.nativeEnumsaccessor. Managed native enums get a migration lifecycle: create/delete, and member addition viaALTER TYPE … ADD VALUE(other member changes are refused with a converting-migration hint). Also authorable in the TypeScript DSL vianativeEnum(name, ...values)+field.column(pg.enum(handle)), with the member union visible intypeof contractwithout an emit. (#906, #944, #949, #970, #935, #958) -
The complete Supabase contract —
@prisma-next/extension-supabasenow ships the full introspected description of everything Supabase owns: everyauthandstoragetable, all native enum types, and the three platform roles (anon,authenticated,service_role), up from the previous 5-table minimum. A secondarydb.asServiceRole().supabase.{sql,orm}admin root reads Supabase-internal tables asservice_role, and the extension ships with docs, a real-Supabase acceptance harness, and a user-facingprisma-next-supabaseskill. (#845, #960, #985, #987) -
PSL language server — a new
prisma-next lspsubcommand serves diagnostics, formatting, completions (types and block templates), semantic highlighting, folding regions, and symbol-table diagnostics over LSP, backed by the fault-tolerant CST parser (which now fully replaces the legacy parser).prisma formatformats PSL from the CLI, and a browser playground wires a Monaco editor to the language server. (#852, #851, #850, #857, #862, #871, #878, #869, #856, #887, #972) -
PSL native scalar lists — scalar-list fields (
String[],Int[], …) lower to native array storage columns instead of a JSONB fallback, end-to-end: author, migrate, and infer, gated on the adapter-reportedscalarListcapability. (#870, #846) -
PSL authors many-to-many — an
N:Mrelation with athroughjunction is now authorable in PSL, completing the M:N surface whose read side landed in 0.14. (#819) -
Per-migration contract snapshots — each applied migration persists its contract snapshot in a 1:1 ledger companion table, and the
Migrationbase class takes typed start/end contract JSON, exposingthis.startContract/this.endContractviews for data-transform migrations. (#908, #879) -
Client-safe static surface — new
@prisma-next/{postgres,sqlite,mongo}/staticentrypoints export<target>Static({ contractJson }), a driver-freeExecutionContextplus derivedenums, query builder,raw, andcontract— safe to import in client bundles. The runtime facades also exposedb.contextanddb.contract. (#888) -
Mongo enums, end-to-end — enums are authorable for MongoDB in PSL and the TypeScript builder, enforced at the database layer via a planner-generated
$jsonSchemavalidator, and typed from a stored value set the same way SQL enums are. The Mongo client also gainsdb.rawanddb.execute(plan). (#834, #900, #880) -
Extension-aware
contract infer—contract inferomits database elements a stack extension pack's contract already describes, and resolves a foreign key into pack-owned space as a qualified cross-space relation (e.g.supabase:auth.AuthUser) instead of re-declaring the pack's tables. (#919) -
Variant-declared relations in the ORM — the
.variant('X')-narrowed accessor surfaces relations the variant model declares (filterable and includable), alongside the base model's relations. (#933, #976) -
Enum
@@typeinference — a PSLenumblock may omit@@type; the codec is inferred from the member values (text for string members, int for integers). (#905) -
@relation(index: false)andinetcolumns — PSL's@relationgains an optionalindexargument for foreign keys whose columns genuinely have no backing index (contract inferemits it automatically), and the Postgres target gains apg/inet@1codec soinetcolumns are authorable asString @db.Inetand inferrable. (#960)
-
@default(false)survives emission — the contract canonicalizer no longer stripsvalue: falsefrom resolved defaults, so a boolean-falsecolumn default is present in the emittedcontract.jsonand round-trips against live introspection. Re-emitting an affected contract changes its storage hash. (#904) -
Mongo reshaping reads decode through codecs — aggregation reads through
$project/$addFieldsstages decode their output fields instead of returning raw BSON (a projected_idnow comes back decoded, not as a rawObjectId). (#897) -
pgbindings resolve by structure — a caller-supplied Pool/Client from a duplicatedpgcopy in a bundle now resolves correctly instead of throwingUnable to determine pg binding typeat boot; newisPgPool/isPgClientguards are exported from@prisma-next/postgres/runtime. (#969) -
Array columns verify cleanly — a scalar-list column's derived schema IR keeps the bare element type with
many: true(previously every list column verifiednot-equalagainst live introspection); Postgres introspection also excludes expression-keyed indexes and no longer collides unique and non-unique indexes over identical columns. (#960) -
Stack-missing migration errors name the failing operation — the error raised when a migration references an operation the stack doesn't provide now says which operation. (#953)
This release reshapes the enum surface (PSL enum is now a domain concept backed by a value-set CHECK constraint, not a native Postgres type), makes the SQL builder always-qualified by namespace, adds native UUID storage on Postgres, ships a new fault-tolerant PSL parser, completes the read side of many-to-many (correlated includes plus some / every / none filters through the junction), and adds a Supabase façade alongside several runtime-class renamings. Most breaking changes have a matching codemod or upgrade recipe.
-
PSL
enumbecomes the domain enum — anenumblock now authors a text-class column whose value set is enforced by a CHECK constraint, not a nativeCREATE TYPE … AS ENUM. Each block must declare@@type("<codec-id>")(typicallypg/text@1) and map members to database values withName = "value". The transitionalenum2keyword is retired (rename toenum— emitted contract is identical). Native enum machinery is deleted:enumType(name, values[])/enumColumnfrom@prisma-next/adapter-postgres/column-types, thepg/enum@1codec, and adoption of native enum types incontract inferare all gone. Databases carrying a native enum type need a one-time converting migration (ALTER column totextUSING::text, add the value-set CHECK,DROP TYPE) —contract inferrefuses native enum types and names them. See the 0.13→0.14 upgrade recipe and the extension-author recipe. (#817)Before:
enum user_type { admin user }
After:
enum user_type { @@type("pg/text@1") admin = "admin" user = "user" }
-
Query builder and ORM are always qualified by namespace — the flat by-bare-name accessors are removed at the builder layer; the Postgres facade exposes the namespaced surface. On Postgres,
db.sql.<table>becomesdb.sql.<namespace>.<table>anddb.orm.<Model>becomesdb.orm.<namespace>.<Model>(publicfor a standard single-schema project). Direct builder calls (sql.<table>,orm.<Model>) migrate the same way. SQLite and Mongo are unaffected — their single-namespace facade keeps the flat surface working. No codemod: the correct namespace is the one each table/model is declared in. The generatedcontract.d.tsalso drops the flat top-levelexport type Models— read models per-namespace asContract['domain']['namespaces']['<namespace>']['models']and re-emit. See the 0.13→0.14 upgrade recipe. (#778)Before:
const users = await db.sql.user.select('id', 'email').build().execute(); const alice = await db.orm.User.find({ where: { id } });
After:
const users = await db.sql.public.user.select('id', 'email').build().execute(); const alice = await db.orm.public.User.find({ where: { id } });
-
UUID field presets renamed by storage encoding —
field.uuid()→field.uuidString(),field.id.uuidv4()→field.id.uuidv4String(),field.id.uuidv7()→field.id.uuidv7String(). The new names describe thechar(36)storage encoding (the emitted codec,sql/char@1, is unchanged). Postgres-nativeuuidcolumns use the newfield.uuidNative()/field.id.uuidv4Native()/field.id.uuidv7Native()presets from@prisma-next/postgres/contract-builder. The rename is mechanical — a colocated codemod ships in the 0.13→0.14 upgrade recipe. (#810)Before:
id: field.id.uuidv7(), externalId: field.uuid(),
After:
id: field.id.uuidv7String(), externalId: field.uuidString(),
-
Postgres migration op factories become methods on
Migration— the bare op factory functions previously exported from@prisma-next/postgres/migration(and the@prisma-next/target-postgres/migrationalias) are removed. Each is now a protected method on thePostgresMigrationbase class — call it asthis.<op>(...). Positional arguments are replaced by a single options object. A codemod ships in the 0.13→0.14 upgrade recipe. (#813)Before:
import { addForeignKey, dropColumn } from '@prisma-next/postgres/migration'; override get operations() { return [ dropColumn('public', 'user', 'legacyName'), addForeignKey('public', 'post', { name: 'post_userId_fkey', columns: ['userId'], references: { schema: 'public', table: 'user', columns: ['id'] } }), ]; }
After:
override get operations() { return [ this.dropColumn({ schema: 'public', table: 'user', column: 'legacyName' }), this.addForeignKey({ schema: 'public', table: 'post', foreignKey: { name: 'post_userId_fkey', columns: ['userId'], references: { schema: 'public', table: 'user', columns: ['id'] } } }), ]; }
-
SQL runtime class renames —
@prisma-next/sql-runtimeexportsabstract class SqlRuntimeBase(previouslySqlRuntime). The bare namesPostgresRuntimeandSqliteRuntimeare now interfaces — the types to depend on in extension and app code. The concrete classes arePostgresRuntimeImpl(from@prisma-next/postgres/runtime) andSqliteRuntimeImpl(from@prisma-next/sqlite/runtime). Code that referenced the class names to subclass them switches to theImplnames. Code using the facade factories (postgres(...),sqlite(...)) is unaffected. (#806) -
createRuntimeremoved from@prisma-next/sql-runtime— use the target facade factory (postgres(...)/sqlite(...)) or construct the target class directly (new PostgresRuntimeImpl({...})/new SqliteRuntimeImpl({...})). The constructor options match whatcreateRuntimeaccepted, exceptstackInstanceis not taken — passadapterdirectly. App code using the facade factories is unaffected. (#806) -
SqlContractSerializerno longer accepts Postgres contracts — the family serializer's entries registry only knows SQL-family built-ins (table,valueSet) and rejects the Postgres-specifictypekey that every Postgres namespace carries. Migration files and app code that deserialize a Postgres-emitted contract must usePostgresContractSerializerfrom@prisma-next/target-postgres/runtime. SQLite and family-only contracts are unaffected. (#812)Before:
import { SqlContractSerializer } from '@prisma-next/family-sql/ir'; const contract = new SqlContractSerializer().deserializeContract(json) as Contract;
After:
import { PostgresContractSerializer } from '@prisma-next/target-postgres/runtime'; const contract = new PostgresContractSerializer().deserializeContract(json) as Contract;
-
Extension authors:
SqlNamespace.entriesis an open dictionary — the closed shape ({ table?, valueSet? }) is gone.entriesis nowReadonly<Record<string, Readonly<Record<string, unknown>>>>, so dot-access like.entries.tableno longer compiles. Read tables via thenamespaceTables(ns)helper from@prisma-next/sql-contract/types, or via bracket notationentries['table']; the concrete class instances still expose typed getters (ns.table). See the extension-author recipe. (#812)
-
Postgres-native UUID storage —
field.uuidNative()/field.id.uuidv4Native()/field.id.uuidv7Native()from@prisma-next/postgres/contract-builderauthor columns backed by the nativeuuidtype. The cross-target*String()presets continue to emitchar(36). (#810) -
Many-to-many reads land —
N:Mrelations through athroughjunction can now be eagerly loaded viainclude()(correlated reads, slice 1) and filtered withsome/every/nonethrough the junction (slice 2). M:N validation arrived in 0.13; the runtime read surface is wired up in this release. (#679, #680) -
Supabase façade —
@prisma-next/extension-supabaseships asupabase()façade andSupabaseRuntimethat composes the cross-contract foreign keys introduced in 0.13 into a runnable extension. (#792) -
Fault-tolerant PSL parser — a new recursive-descent parser produces a full syntax tree (
SourceFile) even when the input contains errors, so editor integrations can report diagnostics and surface partial structure without bailing on the first failure. (#795) -
Custom and parameterized codecs in control-path queries — adapters now honor custom and parameterized codecs when encoding values on the control path (catalog reads, schema-verification queries, migration-state lookups), matching how user-data queries already handled them. (#807)
-
contract inferwrites apragmaheader — inferred PSL contracts now carry apragmablock recording the inference source and options, so re-running infer or auditing a generated schema is unambiguous. (#801) -
Per-namespace typed resolution in the builder — the emitted
contract.d.tsTypeMaps nest by namespace, so the query builder and ORM client resolve each namespace's own columns and fields — fixing same-bare-name models declared in more than one namespace. Re-emit picks up the new shape. (#803) -
Enum input types are exhaustively typed in the emitted
.d.ts— an enum-restricted field's input type renders as the literal member union (matching the output side), so create/update calls are exhaustiveness-checked at compile time. Re-emit picks up the new shape. (#797) -
Typed
db.enums.<namespace>.<Name>accessor — the emitter generates adomainblock incontract.d.tsthat exposes each PSL-authored enum as a literal-typedContractEnumAccessor(values,names,members).contract.jsonis unchanged; re-emit picks up the new types. (#809) -
Enum member defaults via
@default(EnumType.Member)— the PSL interpreter and contract-ts authoring surface resolve a member default to the corresponding database value literal. (#808)
-
sql-orm-clientmodel accessors typed by selected variant — accessing a model on the ORM client narrows the result type to the selected variant rather than the union of all variants. (#790) -
Emitter emits enum input literals — fixes a hole where enum-restricted input types fell back to the codec's broad input type instead of the literal member union. (#797)
-
Un-namespaced Postgres models default to
public— un-namespaced models in a Postgres contract correctly default to thepublicnamespace per ADR 223; the spurious empty__unbound__storage slot is gone. Re-emit picks up the shape change. (#838)
This release makes namespaces a first-class part of the query surface, adds cross-contract foreign keys to the SQL ORM, makes many-to-many a validatable contract shape, introduces a per-object control policy (@@control) that decides what Prisma manages, ships domain enums backed by storage value-sets, and gives the migration CLI a unified graph-tree view across list / log / status / show. Telemetry also flips from opt-in to opt-out. A few changes require a one-time contract re-emit — all are covered by the linked upgrade recipes.
-
Telemetry is now opt-out — anonymous CLI telemetry is collected by default and you opt out, where previously you opted in. Set
PRISMA_NEXT_DISABLE_TELEMETRY=1(orDO_NOT_TRACK=1) to turn it off. Seedocs/Telemetry.mdfor what is collected and every opt-out signal. (#676) -
MTI variant tables materialize a base-PK link column — a PSL
@@base(Parent, "tag")variant that carries its own@@map(and is therefore stored in its own table) now emits a base-PK link column in storage: the variant table gains a copy of the base table's primary-key column(s), a primary key over them, and a cascading foreign key (ON DELETE CASCADE) referencing the base table's primary key. Previously the variant table held only the variant-specific columns with no primary key and no link to its base. This changes the emittedcontract.json/contract.d.tsand the contract'sstorageHash. Re-emit your contract, then plan and apply the matching migration. Variants that share the base table (no own@@map) are unaffected. See the 0.12→0.13 upgrade recipe. (#669)Before (emitted
contract.json, variant tablebug):"bug": { "columns": { "severity": { "codecId": "pg/text@1", "nullable": false } } }
After:
"bug": { "columns": { "id": { "codecId": "sql/char@1", "nullable": false }, "severity": { "codecId": "pg/text@1", "nullable": false } }, "primaryKey": { "columns": ["id"] }, "foreignKeys": [ { "name": "bug_id_fkey", "columns": ["id"], "references": { "table": "task", "columns": ["id"] }, "onDelete": "cascade" } ] }
-
Contract storage IR moved to a namespace envelope — the SQL/Mongo storage IR is now keyed by namespace (
storage.namespaces.<ns>.entries.<kind>), and cross-references are explicit{ namespace, model }objects indomain. Consumer impact is mechanical: re-emit withprisma-next contract emitto pick up the new shape. No codemod or source change is required, but the contract'sstorageHashchanges, so plan and apply a migration afterward. (#715) -
Extension authors: codec-resolution SPI takes a leading
namespaceId—CodecDescriptorRegistry.codecRefForColumn(table, column)is nowcodecRefForColumn(namespaceId, table, column), and the freecodecRefForStorageColumn(storage, table, column)is nowcodecRefForStorageColumn(storage, namespaceId, table, column)(both in@prisma-next/sql-relational-core). Thread the namespace the table lives in through every call site that stampscodeconto AST nodes. There is no codemod — the right namespace is call-site-specific. See the 0.12→0.13 extension-author recipe. (#715)Before:
const ref = descriptors.codecRefForColumn('document', 'embedding');
After:
const ref = descriptors.codecRefForColumn('public', 'document', 'embedding');
-
Extension authors: empty
typeParamsstripped fromstorage.types— the canonicalizer now omitstypeParamsfromstorage.typesentries when it is an empty object (e.g. atypes { Uuid = String @db.Uuid }named-type alias). Runtime behaviour is unchanged, but the emittedcontract.jsonand itsstorageHashdiffer. If your extension shipped acontract.jsonwith"typeParams": {}, re-emit and re-pin your migration baselines. See the 0.12→0.13 extension-author recipe. (#753)
-
Namespace-aware DSL/ORM surface — the typed query and ORM surface now exposes namespaced accessors so models in different namespaces are addressed explicitly and two same-named tables in different namespaces no longer collide. Additive — existing single-namespace code is unchanged. (#720)
-
Many-to-many is now a validatable contract shape —
N:Mrelations carrying athroughjunction descriptor are now a first-class, validatable part of the contract (they previously failed validation). The ORM runtime surface for M:N —.include()across the junction,some/every/nonefilters, and junction writes — is not wired up yet and lands in a follow-up release; nested M:N mutations currently throw. (#669, #678) -
Cross-contract foreign keys — a relation field can reference a model owned by another contract space (e.g.
supabase:auth.AuthUser), with named-type aliases (types { Uuid = String @db.Uuid }) for database-native column types. The planner and verifier resolve the cross-space reference and emit the foreign key, including cascading deletes. See the 0.12→0.13 upgrade recipe for the authoring pattern. (#745, #752, #756, #765)types { Uuid = String @db.Uuid } namespace public { model Profile { id String @id @default(uuid()) username String userId Uuid @unique user supabase:auth.AuthUser @relation(fields: [userId], references: [id], onDelete: Cascade) @@map("profile") } }
-
Per-object control policy (
@@control) — a model or other contract object can declare whether Prisma manages its schema, and a contract can set adefaultControlPolicy. Migration DDL generation and schema verification react to each object's policy, so you can keep externally-owned objects out of Prisma's managed surface. (#717, #711) -
Domain enums with storage value-sets — enums are now a domain concept backed by storage value-sets. On Postgres,
enumblocks lower to a native enum type (CREATE TYPE … AS ENUM); SQL targets without native enum support approximate the allowed values with check constraints. (#750, #755) -
Unified migration graph view in the CLI —
migration list,log,status, andshownow render the migration history as a consistent graph tree with colored lanes, a--legend, and one schema-locked--jsonshape across the read commands.migrate --showpreviews the migration path read-only before you apply it. (#706, #704, #705, #735, #741, #767) -
Readable per-migration ledger — the migration apply ledger is now a per-migration journal, read back as one flat chronological table by
migration log. (#665, #704) -
db.transaction()on the SQLite facade —@prisma-next/sqlitegains a facade-level transaction API (db.transaction(async (tx) => …)), mirroring the Postgres facade. (#737) -
Declarative SPI for extension-contributed PSL blocks — extensions can declare top-level PSL blocks declaratively, and
contract inferround-trips them through a generic PSL printer. (#753, #754, #757) -
@prisma-next/extension-supabase— a new extension package and anexamples/supabasewalking skeleton that wires a cross-contract foreign key from an app model to Supabase'sauthschema. (#746, #765) -
STI variants can declare their own fields — a PSL
@@base(Parent, "tag")variant with no own@@map(single-table inheritance) may now declare its own scalar fields. Each is materialized as a (nullable) column on the shared base table, and the variant no longer emits a stray shadow table. Previously such a contract failed to emit withreferences non-existent column. Existing contracts re-emit identically. (#669) -
Backward cursor pagination —
OrderByItem.reverse()flips an order-by direction for fetching the previous page. (#671) -
Postgres JSON defaults emit a
::jsonb/::jsoncast — JSON column defaults now carry the explicit cast in generated DDL. (#763)
- Constraintless foreign keys are skipped in offline schema projection. (#744)
- Storage-sort comparison is now collation-independent. (#721)
Namespaces become first-class: un-namespaced Postgres models now live in public, the application plane is symmetric with storage, and every cross-namespace reference is explicit. This release also ratifies a version-support policy (Node 24+), simplifies runtime marker verification, closes MongoDB validators by default, and adds raw SQL to the typed builder. Several contract-shape changes require a one-time re-emit — most are mechanical and covered by the linked upgrade recipes.
-
Supported-version floors raised — the supported floor for each dependency is now the latest GA release we test against: Node.js
>=24(declared in every package'sengines), TypeScript>=5.9, PostgreSQL17, and MongoDB8.0. Bump your runtime and toolchain to meet these floors before upgrading. (#659) -
Un-namespaced Postgres models default to
public— models without an explicit namespace now emit under thepublicnamespace instead of the__unbound__sentinel (postgres-unbound-schema→postgres-schema); explicitnamespace unbound { … }still round-trips to__unbound__. Re-emit your contract socontract.json/contract.d.tspick up the new namespace key. See the 0.11→0.12 upgrade recipe. (#662)Before (emitted
contract.json):"storage": { "namespaces": { "__unbound__": { "id": "__unbound__", "kind": "postgres-unbound-schema" } } }
After:
"storage": { "namespaces": { "public": { "id": "public", "kind": "postgres-schema" } } }
-
Symmetric domain plane — models and value objects moved from flat
contract.models/contract.valueObjectstocontract.domain.namespaces.<ns>, and emittedcontract.d.tsexportsModelsviaContractModelsMap<Contract>instead ofContract['models']. Re-emit your contract; consumers reading the flat shape must adopt the namespaced helpers. See the 0.11→0.12 upgrade recipe (extension authors: the extension-author recipe also covers the removal of the@prisma-next/contract/testingsubpath — test factories now live in@prisma-next/test-utils). (#653)Before (consuming emitted
contract.d.ts):type Models = Contract['models'];
After:
type Models = ContractModelsMap<Contract>;
-
Cross-namespace references are explicit
{ namespace, model }pairs — emitted contract roots andrelation.tonow carry an explicit{ namespace, model }object (namespace branded asNamespaceId) rather than a bare model-name string. Re-emit your contract, and update any code that readrelation.to(or a root) as a string to read.model/.namespace. (#600)Before (consuming emitted
contract.d.ts):// relation.to was a bare model-name string readonly to: 'User';
After:
// relation.to is now an explicit { namespace, model } readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'User' };
-
capabilitiesremoved fromdefineContract— thecapabilitiesfield on the first argument ofdefineContract({ … }, …)is gone; capabilities are now contributed automatically by target components and the extension packs inextensionPacks. Delete thecapabilities: { … }block from every call site and re-emit. See the 0.11→0.12 upgrade recipe. (#574)Before:
export const contract = defineContract( { extensionPacks: { pgvector }, capabilities: { postgres: { lateral: true, jsonAgg: true } }, }, ({ field, model }) => { // … model definitions … }, );
After:
export const contract = defineContract( { extensionPacks: { pgvector } }, ({ field, model }) => { // … model definitions … }, );
-
verifyMarkerreplacesverify/RuntimeVerifyOptions— the SQL runtime'sverify: { mode, requireMarker }option is replaced byverifyMarker?: 'onFirstUse' | false(default'onFirstUse'), and the runtime no longer throws on contract-marker drift — it emits onewarn-level log line per runtime instance and proceeds. TheRuntimeVerifyOptionsexport is removed in favour ofVerifyMarkerOption. Migrateverifycall sites and switch fail-fast verification to thedb-verifyCLI. See the 0.11→0.12 upgrade recipe. (#592)Before:
const runtime = createRuntime({ stackInstance, context, driver, verify: { mode: 'onFirstUse', requireMarker: false }, });
After:
const runtime = createRuntime({ stackInstance, context, driver, // verifyMarker omitted — 'onFirstUse' is the default; pass `false` to skip });
-
Migration manifest closed;
labels/hintsremoved — the on-diskmigration.jsonschema is now closed and no longer carrieslabelsorhints; a manifest still holding either key fails to load withINVALID_MANIFEST. Both fields also leave the content-addressed migration identity, somigrationHashchanges. Run the colocated codemod to strip the keys and recompute each hash. See the 0.11→0.12 upgrade recipe. (#615) -
MongoDB emits closed
$jsonSchemavalidators by default — every emitted object schema (collection validators, nested objects, andoneOfbranches) now carriesadditionalProperties: false, and each non-variant Mongo model must resolve to anobjectId_idbefore emit succeeds. Re-emit your Mongo contracts and apply the open→closed validator change (the planner classifies it as destructive). See the 0.11→0.12 upgrade recipe. (#637) -
mongodbis now a user-supplied peer dependency —@prisma-next/driver-mongo,@prisma-next/adapter-mongo, and@prisma-next/mongono longer bundlemongodb; installmongodb@^7yourself as a peer dependency. (#597) -
.distinct(cols)now collapses to one row per group —.distinct(cols)on the SQL ORMCollection(and on nested.include(…)) now keeps a single representative row per(cols)group, matching Prisma semantics; previously it did not collapse when the projection carried other distinguishing columns. No call-site change is required, but query results change — review any logic or fixtures that relied on the old non-collapsing output. Extension authors implementingExprVisitor/ exhaustiveexpr.kindswitches must handle the newWindowFuncExprvariant — see the extension-author recipe. (#576) -
In-repo CipherStash extension removed —
@prisma-next/extension-cipherstashis no longer published from this repo; CipherStash's encrypted-field support now ships from CipherStash's own repository as@cipherstash/prisma-next. Depend on that package instead. (#650)
- Customize where the contract emitter writes via
outputPathinprisma-next.config.tsor--output-pathonprisma-next contract emit. (#584) - Raw SQL in the typed query builder (
rawSql) for Postgres and SQLite, so escape-hatch expressions compose with the rest of the builder. (#594) migration listrewritten to show the complete migration set, ref/graph context, and multi-space output instead of only the migrations along a single chain. (#603)migration graph --treerenders a condensed annotated-tree view of the migration topology. (#658)- Roll back migrations without editing contract source: reverse edges are now plannable and applyable via
--to. (#635) - Single-query include aggregates in the SQL ORM client — counts and aggregates on included relations are fetched in one query rather than fanning out. (#596)
planExecutionIdonRuntimeMiddlewareContext, a fresh per-execute()identity letting middleware correlatebeforeExecuteandafterExecutefor the same call. (#605)- Mongo middleware can rewrite query parameters in
beforeExecutebefore they are encoded, restoring parity with the SQL param-mutator seam. (#652) emptyContract({ target })lets contract-space extensions that contribute only migration invariants (e.g. installing a Postgres extension) omit a contract source instead of hand-authoring an empty one. (#651)
- Mongo: optional fields that are
undefinedare omitted when deserializingcreateIndex, instead of being written out. (#580) - Foreign-key referential actions (
onDelete/onUpdate) are now preserved in the schema IR. (#608) - Mongo
db update: adding an optional field to an existing model now applies cleanly — the validator-widening op is classified and applied correctly instead of being gated or dropped. (#624) - The dev→ship transition is fixed: the first
migration planafterdb updatenow succeeds via ref-paired snapshots and an auto-baseline on an empty graph. (#582) prisma-next initscaffolds into the canonicalsrc/prisma/layout, matching the rest of the framework, so fresh projects start in the expected shape. (#581)- In-process contracts built with
defineContractand passed tocreateExecutionContextnow carry the same adapter + driver capability matrix as CLI-emitted contracts. (#602)
- @xxiaoxiong made their first contribution in #580
- @medz made their first contribution in #608