Skip to content

Latest commit

 

History

History
577 lines (390 loc) · 58 KB

File metadata and controls

577 lines (390 loc) · 58 KB

Changelog

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.

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 entitiescontract emit now materializes each foreign key's constraint/index authoring booleans into separate persisted entities: a foreignKeys[] entry is the referential constraint only, and every backing index (including one backing a foreign key) is its own named indexes[] entry. The authoring surface is unchanged (@relation(index:), TS fk({ constraint, index }), foreignKeyDefaults), and re-running contract emit regenerates the new shape with no source change. TypeScript that read .constraint / .index off a contract's foreignKeys[] entry must read the discrete indexes[] entry instead. No migration or DDL change — the schema the planner and db verify derive is identical. See the 0.15-to-0.16 upgrade recipe and the extension-author recipe. (#989)

    Before:

    "foreignKeys": [ { "source": { "columns": ["user_id"] }, ..., "constraint": true, "index": true } ],
    "indexes": []

    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/@@unique to 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 @unique to userId (or make profile a Profile[] list).

  • contract infer declares identity-column defaults, and db verify --strict now sees them — infer emits @default(autoincrement()) for a Postgres GENERATED ... AS IDENTITY column (previously nothing), and db verify introspecting a live identity column resolves its default to autoincrement() too. If you run db verify --strict against a table with an identity column whose contract predates this fix, verify reports that default as an unexpected extra — re-run contract infer for the affected table, or add @default(autoincrement()) by hand. Without --strict, nothing changes. (#1011)

  • contract infer back-relation names no longer double-pluralize — the hand-rolled pluralization rule turned already-plural table names into sessionses; infer now uses real inflection (sessions stays sessions, status still becomes statuses). Already-generated .prisma files are untouched, but the next contract infer run 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-supabase no longer exports ./test/utils — the bootstrapSupabaseShim subpath 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 for temporal.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") — which contract infer already printed — now emits instead of throwing unregistered index type. (#1011)

Fixes

  • Using @prisma-next/extension-supabase against 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 verify no longer reports phantom missing constraints; db.asUser(jwt) supports the ES256/JWKS signing current Supabase uses; and db.asServiceRole() queries succeed with the grants a real project provides. (#997)
  • More contract infer round-trip fixes: a plain Decimal field on Postgres no longer throws CODEC_PARAMETERIZATION_MISMATCH at connect, the 1:1 back-relation shape infer prints is accepted by emit, literal-shaped dbgenerated(...) defaults compare equal in db verify instead 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-backed deleteAll now 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.reason is removed — discriminate via the presence of expected/actual, or the issueOutcome helper from @prisma-next/framework-components/control. (#992)

v0.15.0

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.

Breaking changes

  • 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 bytea include fields return Uint8Array instead of \x-prefixed hex text, timestamp fields return Date instead of strings, and custom codec-backed fields return whatever the codec's decodeJson produces. Custom SQL codec authors: encodeJson / decodeJson now 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 --json reports a single schema.issues list — the split schema.issues / schema.schemaDiffIssues pair collapses into one schema.issues array of { path, reason, message, expected?, actual? }, and the retired outcome field is replaced by reason ('missing''not-found', 'extra''not-expected', 'mismatch''not-equal'). The same collapse applies to schema.warnings. Update scripts or CI steps that read schemaDiffIssues or 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 @@rls on the target model — RLS enablement is an explicit, authored table attribute. A policy_* block's target model must declare @@rls, or contract emit fails with PSL_EXTENSION_TARGET_MODEL_MISSING_ATTRIBUTE. Plan semantics follow the marker: a marked table with RLS off plans ENABLE ROW LEVEL SECURITY, removing every policy keeps RLS enabled (fail-closed deny-all), and removing @@rls plans DISABLE ROW LEVEL SECURITY (requires the destructive allowance). Renaming only a policy's name plans a single ALTER POLICY … RENAME TO instead of drop+create. Extension authors constructing PostgresTableSchemaNode by hand must supply the now-required rlsEnabled boolean. (#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, so prismaContract(...) / defineContract(...) from @prisma-next/sql-contract-psl / @prisma-next/sql-contract-ts need the target's namespace factory (postgresCreateNamespace / sqliteCreateNamespace); target-pack defineContract wrappers already supply it, so app authors are unaffected. SqlNamespace is now an abstract class; buildSqlNamespace, buildSqlNamespaceMap, SqlBoundNamespace, and SqlUnboundNamespace are removed, and hand-written namespace literals carry the target kind (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 verify now run on one generic node differ. collectSqlSchemaIssues / collectSqlSchemaIssuesPerNamespace, diffPostgresDatabaseSchema, and SqlControlTargetDescriptor.diffDatabaseSchema are removed (use diffSchemas or a target's buildXPlanDiff); MigrationPlanner.plan()'s keepDiffIssue predicate is replaced by an ownership oracle; the issue types BaseSchemaIssue / SchemaIssue / EnumValuesChangedIssue are gone — SchemaDiffIssue is the single issue shape everywhere, including the codec verifyType hook; and graphWalkStrategy is renamed resolveRecordedPath in @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 deriveJsonSchema sources enums from value sets — the fourth argument of deriveJsonSchema / derivePolymorphicJsonSchema changes from a domain-enum map to a value-set map (contract.storage.namespaces[<ns>].entries.valueSet). Callers through mongoContract(...) / defineContract(...) need no change. (#900)

  • Extension authors: ScalarFieldState's first generic is the column descriptorScalarFieldState<'pg/text@1', …> becomes ScalarFieldState<ColumnTypeDescriptor<'pg/text@1'>, …>, so field states preserve the whole descriptor type (including native-enum member tuples). Built contract types also keep literal nativeType / typeParams instead of widening to string. (#958)

  • Extension authors: native_enum entities serialize into contract.json, keyed by physical type name — packs declaring native Postgres enums must re-emit their bundled contract so the entries.native_enum maps land in the published artifacts (this is what lets a consumer's contract infer subtract 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)

Features

  • Postgres row-level security, end-to-end — PSL gains policy_select, policy_insert, policy_update, policy_delete, and policy_all blocks (with using / withCheck predicates and per-role targeting), the @@rls enablement attribute, and standalone role declarations inside namespace unbound { }. migration plan plans the full lifecycle (ENABLE / DISABLE ROW LEVEL SECURITY, policy create/drop, rename via ALTER POLICY), and db verify fails 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 enumsCREATE TYPE … AS ENUM types are first-class again, this time as explicit entities. External types the database already owns (e.g. Supabase's auth.aal_level) are declared via native_enum blocks, typed as member-value literal unions, adopted by contract infer, and read at runtime through the new Postgres-only db.nativeEnums accessor. Managed native enums get a migration lifecycle: create/delete, and member addition via ALTER TYPE … ADD VALUE (other member changes are refused with a converting-migration hint). Also authorable in the TypeScript DSL via nativeEnum(name, ...values) + field.column(pg.enum(handle)), with the member union visible in typeof contract without an emit. (#906, #944, #949, #970, #935, #958)

  • The complete Supabase contract@prisma-next/extension-supabase now ships the full introspected description of everything Supabase owns: every auth and storage table, all native enum types, and the three platform roles (anon, authenticated, service_role), up from the previous 5-table minimum. A secondary db.asServiceRole().supabase.{sql,orm} admin root reads Supabase-internal tables as service_role, and the extension ships with docs, a real-Supabase acceptance harness, and a user-facing prisma-next-supabase skill. (#845, #960, #985, #987)

  • PSL language server — a new prisma-next lsp subcommand 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 format formats 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-reported scalarList capability. (#870, #846)

  • PSL authors many-to-many — an N:M relation with a through junction 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 Migration base class takes typed start/end contract JSON, exposing this.startContract / this.endContract views for data-transform migrations. (#908, #879)

  • Client-safe static surface — new @prisma-next/{postgres,sqlite,mongo}/static entrypoints export <target>Static({ contractJson }), a driver-free ExecutionContext plus derived enums, query builder, raw, and contract — safe to import in client bundles. The runtime facades also expose db.context and db.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 $jsonSchema validator, and typed from a stored value set the same way SQL enums are. The Mongo client also gains db.raw and db.execute(plan). (#834, #900, #880)

  • Extension-aware contract infercontract infer omits 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 @@type inference — a PSL enum block may omit @@type; the codec is inferred from the member values (text for string members, int for integers). (#905)

  • @relation(index: false) and inet columns — PSL's @relation gains an optional index argument for foreign keys whose columns genuinely have no backing index (contract infer emits it automatically), and the Postgres target gains a pg/inet@1 codec so inet columns are authorable as String @db.Inet and inferrable. (#960)

Fixes

  • @default(false) survives emission — the contract canonicalizer no longer strips value: false from resolved defaults, so a boolean-false column default is present in the emitted contract.json and 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 / $addFields stages decode their output fields instead of returning raw BSON (a projected _id now comes back decoded, not as a raw ObjectId). (#897)

  • pg bindings resolve by structure — a caller-supplied Pool/Client from a duplicated pg copy in a bundle now resolves correctly instead of throwing Unable to determine pg binding type at boot; new isPgPool / isPgClient guards 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 verified not-equal against 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)

New contributors

v0.14.0

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.

Breaking changes

  • PSL enum becomes the domain enum — an enum block now authors a text-class column whose value set is enforced by a CHECK constraint, not a native CREATE TYPE … AS ENUM. Each block must declare @@type("<codec-id>") (typically pg/text@1) and map members to database values with Name = "value". The transitional enum2 keyword is retired (rename to enum — emitted contract is identical). Native enum machinery is deleted: enumType(name, values[]) / enumColumn from @prisma-next/adapter-postgres/column-types, the pg/enum@1 codec, and adoption of native enum types in contract infer are all gone. Databases carrying a native enum type need a one-time converting migration (ALTER column to text USING ::text, add the value-set CHECK, DROP TYPE) — contract infer refuses 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> becomes db.sql.<namespace>.<table> and db.orm.<Model> becomes db.orm.<namespace>.<Model> (public for 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 generated contract.d.ts also drops the flat top-level export type Models — read models per-namespace as Contract['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 encodingfield.uuid()field.uuidString(), field.id.uuidv4()field.id.uuidv4String(), field.id.uuidv7()field.id.uuidv7String(). The new names describe the char(36) storage encoding (the emitted codec, sql/char@1, is unchanged). Postgres-native uuid columns use the new field.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/migration alias) are removed. Each is now a protected method on the PostgresMigration base class — call it as this.<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-runtime exports abstract class SqlRuntimeBase (previously SqlRuntime). The bare names PostgresRuntime and SqliteRuntime are now interfaces — the types to depend on in extension and app code. The concrete classes are PostgresRuntimeImpl (from @prisma-next/postgres/runtime) and SqliteRuntimeImpl (from @prisma-next/sqlite/runtime). Code that referenced the class names to subclass them switches to the Impl names. Code using the facade factories (postgres(...), sqlite(...)) is unaffected. (#806)

  • createRuntime removed 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 what createRuntime accepted, except stackInstance is not taken — pass adapter directly. App code using the facade factories is unaffected. (#806)

  • SqlContractSerializer no longer accepts Postgres contracts — the family serializer's entries registry only knows SQL-family built-ins (table, valueSet) and rejects the Postgres-specific type key that every Postgres namespace carries. Migration files and app code that deserialize a Postgres-emitted contract must use PostgresContractSerializer from @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.entries is an open dictionary — the closed shape ({ table?, valueSet? }) is gone. entries is now Readonly<Record<string, Readonly<Record<string, unknown>>>>, so dot-access like .entries.table no longer compiles. Read tables via the namespaceTables(ns) helper from @prisma-next/sql-contract/types, or via bracket notation entries['table']; the concrete class instances still expose typed getters (ns.table). See the extension-author recipe. (#812)

Features

  • Postgres-native UUID storagefield.uuidNative() / field.id.uuidv4Native() / field.id.uuidv7Native() from @prisma-next/postgres/contract-builder author columns backed by the native uuid type. The cross-target *String() presets continue to emit char(36). (#810)

  • Many-to-many reads landN:M relations through a through junction can now be eagerly loaded via include() (correlated reads, slice 1) and filtered with some / every / none through 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-supabase ships a supabase() façade and SupabaseRuntime that 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 infer writes a pragma header — inferred PSL contracts now carry a pragma block 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.ts TypeMaps 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 a domain block in contract.d.ts that exposes each PSL-authored enum as a literal-typed ContractEnumAccessor (values, names, members). contract.json is 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)

Fixes

  • sql-orm-client model 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 the public namespace per ADR 223; the spurious empty __unbound__ storage slot is gone. Re-emit picks up the shape change. (#838)

v0.13.0

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.

Breaking changes

  • 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 (or DO_NOT_TRACK=1) to turn it off. See docs/Telemetry.md for 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 emitted contract.json / contract.d.ts and the contract's storageHash. 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 table bug):

    "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 in domain. Consumer impact is mechanical: re-emit with prisma-next contract emit to pick up the new shape. No codemod or source change is required, but the contract's storageHash changes, so plan and apply a migration afterward. (#715)

  • Extension authors: codec-resolution SPI takes a leading namespaceIdCodecDescriptorRegistry.codecRefForColumn(table, column) is now codecRefForColumn(namespaceId, table, column), and the free codecRefForStorageColumn(storage, table, column) is now codecRefForStorageColumn(storage, namespaceId, table, column) (both in @prisma-next/sql-relational-core). Thread the namespace the table lives in through every call site that stamps codec onto 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 typeParams stripped from storage.types — the canonicalizer now omits typeParams from storage.types entries when it is an empty object (e.g. a types { Uuid = String @db.Uuid } named-type alias). Runtime behaviour is unchanged, but the emitted contract.json and its storageHash differ. If your extension shipped a contract.json with "typeParams": {}, re-emit and re-pin your migration baselines. See the 0.12→0.13 extension-author recipe. (#753)

Features

  • 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 shapeN:M relations carrying a through junction 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/none filters, 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 a defaultControlPolicy. 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, enum blocks 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 CLImigration list, log, status, and show now render the migration history as a consistent graph tree with colored lanes, a --legend, and one schema-locked --json shape across the read commands. migrate --show previews 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/sqlite gains 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 infer round-trips them through a generic PSL printer. (#753, #754, #757)

  • @prisma-next/extension-supabase — a new extension package and an examples/supabase walking skeleton that wires a cross-contract foreign key from an app model to Supabase's auth schema. (#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 with references non-existent column. Existing contracts re-emit identically. (#669)

  • Backward cursor paginationOrderByItem.reverse() flips an order-by direction for fetching the previous page. (#671)

  • Postgres JSON defaults emit a ::jsonb / ::json cast — JSON column defaults now carry the explicit cast in generated DDL. (#763)

Fixes

  • Constraintless foreign keys are skipped in offline schema projection. (#744)
  • Storage-sort comparison is now collation-independent. (#721)

v0.12.0

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.

Breaking changes

  • 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's engines), TypeScript >=5.9, PostgreSQL 17, and MongoDB 8.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 the public namespace instead of the __unbound__ sentinel (postgres-unbound-schemapostgres-schema); explicit namespace unbound { … } still round-trips to __unbound__. Re-emit your contract so contract.json / contract.d.ts pick 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.valueObjects to contract.domain.namespaces.<ns>, and emitted contract.d.ts exports Models via ContractModelsMap<Contract> instead of Contract['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/testing subpath — 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 and relation.to now carry an explicit { namespace, model } object (namespace branded as NamespaceId) rather than a bare model-name string. Re-emit your contract, and update any code that read relation.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' };
  • capabilities removed from defineContract — the capabilities field on the first argument of defineContract({ … }, …) is gone; capabilities are now contributed automatically by target components and the extension packs in extensionPacks. Delete the capabilities: { … } 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 …
      },
    );
  • verifyMarker replaces verify / RuntimeVerifyOptions — the SQL runtime's verify: { mode, requireMarker } option is replaced by verifyMarker?: 'onFirstUse' | false (default 'onFirstUse'), and the runtime no longer throws on contract-marker drift — it emits one warn-level log line per runtime instance and proceeds. The RuntimeVerifyOptions export is removed in favour of VerifyMarkerOption. Migrate verify call sites and switch fail-fast verification to the db-verify CLI. 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/hints removed — the on-disk migration.json schema is now closed and no longer carries labels or hints; a manifest still holding either key fails to load with INVALID_MANIFEST. Both fields also leave the content-addressed migration identity, so migrationHash changes. Run the colocated codemod to strip the keys and recompute each hash. See the 0.11→0.12 upgrade recipe. (#615)

  • MongoDB emits closed $jsonSchema validators by default — every emitted object schema (collection validators, nested objects, and oneOf branches) now carries additionalProperties: false, and each non-variant Mongo model must resolve to an objectId _id before 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)

  • mongodb is now a user-supplied peer dependency@prisma-next/driver-mongo, @prisma-next/adapter-mongo, and @prisma-next/mongo no longer bundle mongodb; install mongodb@^7 yourself as a peer dependency. (#597)

  • .distinct(cols) now collapses to one row per group.distinct(cols) on the SQL ORM Collection (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 implementing ExprVisitor / exhaustive expr.kind switches must handle the new WindowFuncExpr variant — see the extension-author recipe. (#576)

  • In-repo CipherStash extension removed@prisma-next/extension-cipherstash is 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)

Features

  • Customize where the contract emitter writes via outputPath in prisma-next.config.ts or --output-path on prisma-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 list rewritten 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 --tree renders 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)
  • planExecutionId on RuntimeMiddlewareContext, a fresh per-execute() identity letting middleware correlate beforeExecute and afterExecute for the same call. (#605)
  • Mongo middleware can rewrite query parameters in beforeExecute before 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)

Fixes

  • Mongo: optional fields that are undefined are omitted when deserializing createIndex, 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 plan after db update now succeeds via ref-paired snapshots and an auto-baseline on an empty graph. (#582)
  • prisma-next init scaffolds into the canonical src/prisma/ layout, matching the rest of the framework, so fresh projects start in the expected shape. (#581)
  • In-process contracts built with defineContract and passed to createExecutionContext now carry the same adapter + driver capability matrix as CLI-emitted contracts. (#602)

New contributors