v8.0.0-rc.1
Pre-releasev8.0.0-rc.1
This is the first release on the v8 release-candidate line: releases are now versioned 8.0.0-rc.N instead of 0.x minors. It also makes every aggregate read back through the codec its target declares — count() returns a bigint — splits the SQL driver interface into a row-streaming call and a statistics call, and fixes four defects in query planning, emit, and driver error reporting.
The v8 release-candidate line
Releases are now versioned 8.0.0-rc.1, 8.0.0-rc.2, and so on, with the counter advancing on every release. "The v8 RC" is the product name; the number underneath iterates freely, so there is no promise that the last RC before 8.0.0 final is numbered rc.1. There are no further 0.x minors. The policy is written up in docs/oss/versioning.md. (#29899)
For every package this repository publishes, latest keeps tracking the newest release, RC included. These package names have no pre-v8 stable audience to protect — a bare npm install of one of them was already an early-access install, and still is. The bare prisma package is not published from this repository; its v8 CLI shim lives in prisma/prisma-cli.
Existing installs are not moved onto the RC line by npm update. Lockfiles pin resolved versions, and a ^0.x range can never match a 8.0.0-rc.N pre-release, because pre-releases do not satisfy stable ranges. Only a fresh install, or an explicit version change on your side, lands on the RC.
Development builds move to the same line: every push to main that does not change the root version publishes 8.0.0-rc.X-dev.N under the dev dist-tag.
An RC respin may still contain breaking changes. Until 8.0.0 final ships, the pre-1.0 latitude documented in docs/oss/versioning.md carries over: a new rc.N may remove or rename APIs, change the semantics of existing ones, or change the contract format. Read the breaking-changes section of each release before you upgrade.
Breaking changes
-
Aggregate results carry the codec their target declares — an aggregate is now read back through the codec its target declares for that result rather than through whatever the driver handed over, so aggregate application types change.
count()is abiginton both PostgreSQL and SQLite, at the top level and inside an include, and an empty relation reads0n. On PostgreSQL,sumoverint2/int4widens to abigint, whilesum(int8)andavgover any integer arenumericand read as exact decimal strings;min/maxkeep the column's own type, except overvarchar, which returnstext. On SQLite,sumover an integer column is abigintandavgis always anumber. Sweep your code for equality and arithmetic against an aggregate result (count === 2is false whencountis2n) and forJSON.stringifyover one (it throws on a bigint).having(...)operands are the exception and stay plain numbers — they are compared inside SQL and never cross a codec. Regenerate your contracts (prisma-next contract emit):contract.d.tsgains anAggregateTypesblock that both the ORM and the SQL builder resolve result types from, and against an older contract an aggregate resolves toneverin the ORM andunknownin the SQL builder. The type is not the only guard: an aggregate whose operation and input codec the composed target does not declare is rejected before the query runs, with the error codeORM.AGGREGATE_UNSUPPORTED. See the upgrade recipe and the extension-author recipe. (#29867)Before:
const rows = await posts.include('comments', (comments) => comments.count()).all(); rows[0].comments === 2; // number; 0 when the relation is empty
After:
const rows = await posts.include('comments', (comments) => comments.count()).all(); rows[0].comments === 2n; // bigint; 0n when the relation is empty
-
The SQL driver interface splits row streaming from statement statistics —
SqlQueryable(exported from@internal/sql-relational-core/ast) is now two methods wide:query()streams rows andexecute()returns{ affectedRows }. The separate prepared-execution method is gone; a prepared plan is expressed by an optionalpreparedStatementHandleon the request instead, and a driver branches on whether that property isundefined. Application code, query results, and the contract format are unaffected — this only matters if you implement or wrapSqlQueryableyourself, in which case update your implementation to the two-method shape. There is no upgrade recipe entry for this; the change is the interface itself. (#29907)Before:
interface SqlQueryable { execute<Row>(request: SqlExecuteRequest): AsyncIterable<Row>; executePrepared<Row>(request: PreparedExecuteRequest): AsyncIterable<Row>; query<Row>(sql: string, params?: readonly unknown[]): Promise<SqlQueryResult<Row>>; }
After:
interface SqlQueryable { query<Row>(request: SqlExecuteRequest): AsyncIterable<Row>; execute(request: SqlExecuteRequest): Promise<SqlStatementStats>; }
Features
prisma-next initinstalls oneprisma-8skill instead of eleven per-workflow skills, and removes the retired skill directories from every agent's install root on each run. Each skill is now installed by name —prisma-8,prisma-next-upgrade, andprisma-8-extension-upgrade— rather than by matching a wildcard against a directory, so a new skill landing beside them is not picked up by accident. (#29853)
Fixes
- A column, table, or model mapped to a name that is not a bare TypeScript identifier —
@map("has space"),@@map("data rows")— now emits a quoted property key incontract.d.tsinstead of producing a syntactically invalid file that killedcontract emit. String literals in emitted TypeScript also survive control characters and line separators, which previously produced the same failure by a different route. (#29889, #29898) - Nested
some/every/nonepredicates over a self-referential relation now keep a distinct SQL alias at every level, so an inner scope no longer shadows the parent it is supposed to correlate against. This covers one-to-one, many-to-one, one-to-many, implicit many-to-many, and explicit-junction many-to-many relations in both directions, and relations whose physical tables share a bare name across namespaces. (#29900) - Scalar reducers on a many-to-many include —
count(),sum(),avg(),min(),max()— now traverse the junction table instead of emitting a predicate against a foreign-key column that only exists on the junction, so a filtered relation count over a many-to-many relation returns the right number. (#29888) - A failed retry of a stale PostgreSQL prepared statement now surfaces a structured error envelope with the code
DRIVER.PREPARE_FAILED, carrying the normalized driver error as its cause, instead of an unlabelled failure. (#29907)