From f1625725d20965bbe72775a9802ae727fef5b96d Mon Sep 17 00:00:00 2001 From: willbot Date: Wed, 26 Aug 2026 14:08:02 +0200 Subject: [PATCH 1/2] refactor: adopt the Prisma ORM composer API names MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Composer 0.15.0 renames its ORM entrypoint (prisma/composer#265): the `/prisma-next` subpath becomes `/orm`, `pnPostgres()` becomes `postgres()`, and `pnContract()` becomes `dataContract()`. Update the scaffold templates and pin composer 0.15.0; rename the internal `supportsPrismaNext` helper to `supportsPrisma`. The Deno path deliberately keeps the `prisma-next` npm CLI (ORM-only entrypoint) and its generated `prisma-next.config.ts`/`prisma-next.md` until the consolidated CLI is Deno-compatible — those references are unchanged. Signed-off-by: willbot Signed-off-by: Will Madden --- src/commands/create.ts | 4 ++-- src/constants/dependencies.ts | 8 ++++---- src/utils/node-version.ts | 2 +- templates/create/_shared/module.ts.hbs | 4 ++-- templates/create/_shared/service.ts.hbs | 4 ++-- templates/create/_shared/src/prisma/composer.ts.hbs | 4 ++-- tests/dependencies.test.ts | 6 +++--- tests/e2e/create-prisma.e2e.test.ts | 4 ++-- tests/install.test.ts | 4 ++-- tests/node-version.test.ts | 8 ++++---- 10 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/commands/create.ts b/src/commands/create.ts index 4ef4d56..5ad25e7 100644 --- a/src/commands/create.ts +++ b/src/commands/create.ts @@ -27,7 +27,7 @@ import { import { getCreatePrismaIntro } from "../ui/branding"; import { resolveExecutionSettings } from "../ui/output"; import { getErrorMessage } from "../utils/errors"; -import { getUnsupportedNodeMessage, supportsPrismaNext } from "../utils/node-version"; +import { getUnsupportedNodeMessage, supportsPrisma } from "../utils/node-version"; const DEFAULT_PROJECT_NAME = "my-app"; const DEFAULT_TEMPLATE: CreateTemplate = "minimal"; @@ -221,7 +221,7 @@ export async function runCreateCommand( if (input.json && input.verbose) { throw new Error("--verbose cannot be used with --json because JSON mode is output-only."); } - if (!supportsPrismaNext()) { + if (!supportsPrisma()) { const message = getUnsupportedNodeMessage(); cancel(message, { output }); process.exitCode = 1; diff --git a/src/constants/dependencies.ts b/src/constants/dependencies.ts index b0fc177..8fae75d 100644 --- a/src/constants/dependencies.ts +++ b/src/constants/dependencies.ts @@ -3,8 +3,8 @@ import type { CreateTemplate, PackageManager } from "../types"; export const dependencyVersionMap = { "@astrojs/node": "^10.0.2", "@elysiajs/node": "^1.4.5", - "@prisma/composer": "0.15.0", - "@prisma/composer-prisma-cloud": "0.15.0", + "@prisma/composer": "0.16.0", + "@prisma/composer-prisma-cloud": "0.16.0", "@prisma/orm-mongo": "8.0.0-rc.8", // Must match @prisma/composer-prisma-cloud's exact peerDependency. "@prisma/orm-postgres": "8.0.0-rc.8", @@ -18,7 +18,7 @@ export const dependencyVersionMap = { mongodb: "^7.1.0", "mongodb-memory-server": "^11.1.0", nitro: "^3.0.260610-beta", - prisma: "8.0.0-rc.11", + prisma: "8.0.0-rc.12", tsx: "^4.21.0", typescript: "^5.9.3", } as const; @@ -29,7 +29,7 @@ export const dependencyVersionMap = { // above: the CLI bundles its own copies of @prisma/composer-cli and // @prisma/orm-toolchain, and those must match the @prisma/composer* and // @prisma/orm-* versions this map installs into the project. -export const PRISMA_PLATFORM_CLI_PACKAGE = "prisma@8.0.0-rc.11"; +export const PRISMA_PLATFORM_CLI_PACKAGE = "prisma@8.0.0-rc.12"; // Deno runs the same pinned consolidated CLI. The former `prisma-next` // fallback is dead: under Deno the bare `npm:prisma-next` specifier resolves // to the highest non-prerelease version (0.12.0, frozen), which cannot emit diff --git a/src/utils/node-version.ts b/src/utils/node-version.ts index dbdf2f3..c8ffb55 100644 --- a/src/utils/node-version.ts +++ b/src/utils/node-version.ts @@ -5,7 +5,7 @@ function parseVersion(version: string): [number, number, number] { return [Number(major), Number(minor), Number.parseInt(patch, 10)]; } -export function supportsPrismaNext(nodeVersion = process.versions.node): boolean { +export function supportsPrisma(nodeVersion = process.versions.node): boolean { const current = parseVersion(nodeVersion); for (let index = 0; index < MINIMUM_NODE_VERSION.length; index += 1) { if (current[index]! > MINIMUM_NODE_VERSION[index]!) return true; diff --git a/templates/create/_shared/module.ts.hbs b/templates/create/_shared/module.ts.hbs index 9dc0d34..6c24897 100644 --- a/templates/create/_shared/module.ts.hbs +++ b/templates/create/_shared/module.ts.hbs @@ -1,7 +1,7 @@ {{#unless (eq packageManager "deno")}} import { module } from "@prisma/composer"; {{#if (eq provider "postgres")}} -import { pnPostgres } from "@prisma/composer-prisma-cloud/prisma-next"; +import { postgres } from "@prisma/composer-prisma-cloud/orm"; import { appContract } from "./src/prisma/composer.ts"; {{else}} @@ -12,7 +12,7 @@ import app from "./service.ts"; export default module("{{projectName}}", ({ provision }) => { {{#if (eq provider "postgres")}} const database = provision( - pnPostgres({ + postgres({ name: "database", contract: appContract, config: "./prisma.config.ts", diff --git a/templates/create/_shared/service.ts.hbs b/templates/create/_shared/service.ts.hbs index 8af3e00..3866e57 100644 --- a/templates/create/_shared/service.ts.hbs +++ b/templates/create/_shared/service.ts.hbs @@ -10,7 +10,7 @@ import { type } from "arktype"; {{/if}} import { compute } from "@prisma/composer-prisma-cloud"; {{#if (eq provider "postgres")}} -import { pnPostgres } from "@prisma/composer-prisma-cloud/prisma-next"; +import { postgres } from "@prisma/composer-prisma-cloud/orm"; import { appContract } from "./src/prisma/composer.ts"; {{/if}} @@ -19,7 +19,7 @@ export default compute({ name: "app", deps: { {{#if (eq provider "postgres")}} - database: pnPostgres(appContract), + database: postgres(appContract), {{/if}} }, {{#if (eq provider "mongo")}} diff --git a/templates/create/_shared/src/prisma/composer.ts.hbs b/templates/create/_shared/src/prisma/composer.ts.hbs index 4dcaebb..df8ae16 100644 --- a/templates/create/_shared/src/prisma/composer.ts.hbs +++ b/templates/create/_shared/src/prisma/composer.ts.hbs @@ -1,10 +1,10 @@ {{#unless (eq packageManager "deno")}} {{#if (eq provider "postgres")}} -import { pnContract } from "@prisma/composer-prisma-cloud/prisma-next"; +import { dataContract } from "@prisma/composer-prisma-cloud/orm"; import type { Contract } from "./{{#if (eq authoring "typescript")}}generated/{{/if}}contract.d.ts"; import contractJson from "./{{#if (eq authoring "typescript")}}generated/{{/if}}contract.json" with { type: "json" }; -export const appContract = pnContract(contractJson); +export const appContract = dataContract(contractJson); {{/if}} {{/unless}} diff --git a/tests/dependencies.test.ts b/tests/dependencies.test.ts index f96e5c6..f6f021a 100644 --- a/tests/dependencies.test.ts +++ b/tests/dependencies.test.ts @@ -6,9 +6,9 @@ describe("Prisma 8 dependency versions", () => { test("uses the selected Prisma 8 and Composer releases", () => { expect(getDependencyVersion("@prisma/orm-postgres")).toBe("8.0.0-rc.8"); expect(getDependencyVersion("@prisma/orm-mongo")).toBe("8.0.0-rc.8"); - expect(getDependencyVersion("@prisma/composer")).toBe("0.15.0"); - expect(getDependencyVersion("@prisma/composer-prisma-cloud")).toBe("0.15.0"); - expect(getDependencyVersion("prisma")).toBe("8.0.0-rc.11"); + expect(getDependencyVersion("@prisma/composer")).toBe("0.16.0"); + expect(getDependencyVersion("@prisma/composer-prisma-cloud")).toBe("0.16.0"); + expect(getDependencyVersion("prisma")).toBe("8.0.0-rc.12"); expect(getDependencyVersion("alchemy")).toBe("2.0.0-beta.74"); expect(getDependencyVersion("effect")).toBe("4.0.0-rc.112"); }); diff --git a/tests/e2e/create-prisma.e2e.test.ts b/tests/e2e/create-prisma.e2e.test.ts index 1984259..759f7c0 100644 --- a/tests/e2e/create-prisma.e2e.test.ts +++ b/tests/e2e/create-prisma.e2e.test.ts @@ -354,11 +354,11 @@ describe("create-prisma e2e", () => { expect( await pathExists(path.join(projectDir, ".claude/skills/prisma-composer/SKILL.md")), ).toBe(true); - expect(packageJson.devDependencies.prisma).toBe("8.0.0-rc.11"); + expect(packageJson.devDependencies.prisma).toBe("8.0.0-rc.12"); expect(packageJson.scripts.postinstall).toBe("prisma skills sync || exit 0"); expect(packageJson.scripts.deploy).toContain("bun run composer:deploy"); expect(packageJson.overrides.effect).toBe("4.0.0-rc.112"); - expect(moduleSource).toContain("pnPostgres({"); + expect(moduleSource).toContain("postgres({"); expect(dbSource).toContain("service.load().database.client"); await runCommand(projectDir, ["bun", "run", "build"]); diff --git a/tests/install.test.ts b/tests/install.test.ts index ec7337a..a84bfb6 100644 --- a/tests/install.test.ts +++ b/tests/install.test.ts @@ -240,7 +240,7 @@ describe("generated templates", () => { ); } if (provider === "postgres") { - expect(moduleSource).toContain("pnPostgres({"); + expect(moduleSource).toContain("postgres({"); expect(moduleSource).toContain('config: "./prisma.config.ts"'); expect(prismaConfig).toContain("connection: process.env.DATABASE_URL!"); expect(seedSource).toContain("conflictOn: { email: user.email }"); @@ -259,7 +259,7 @@ describe("generated templates", () => { expect(composerSource).toContain( 'import type { Contract } from "./contract.d.ts";', ); - expect(composerSource).toContain("pnContract(contractJson)"); + expect(composerSource).toContain("dataContract(contractJson)"); } } else { expect(moduleSource).toContain('envSecret("MONGODB_URL")'); diff --git a/tests/node-version.test.ts b/tests/node-version.test.ts index 97189f6..8391b21 100644 --- a/tests/node-version.test.ts +++ b/tests/node-version.test.ts @@ -1,12 +1,12 @@ import { describe, expect, test } from "bun:test"; -import { getUnsupportedNodeMessage, supportsPrismaNext } from "../src/utils/node-version"; +import { getUnsupportedNodeMessage, supportsPrisma } from "../src/utils/node-version"; describe("Prisma 8 Node compatibility", () => { test("requires Node 22.18 or newer", () => { - expect(supportsPrismaNext("22.17.9")).toBe(false); - expect(supportsPrismaNext("22.18.0")).toBe(true); - expect(supportsPrismaNext("24.0.0")).toBe(true); + expect(supportsPrisma("22.17.9")).toBe(false); + expect(supportsPrisma("22.18.0")).toBe(true); + expect(supportsPrisma("24.0.0")).toBe(true); }); test("returns an actionable message", () => { From 14143030fd3d1e357c9bec34120c2ca5f083c971 Mon Sep 17 00:00:00 2001 From: willbot Date: Wed, 26 Aug 2026 15:42:25 +0200 Subject: [PATCH 2/2] Fix the Composer-backed scaffold under orm-postgres 8.0.0-rc.8 Three fixes uncovered by the Composer-backed e2e test: - The rc.8 query builder renamed .take() to .limit(); update the postgres branch of the users template. - The rc.8 runtime generates timestamp defaults (instantNow) through the global Temporal object, which no stable Node or Bun ships. Without it the scaffolded app returns 500 on every request because the seed upsert cannot produce updatedAt. Add temporal-polyfill to postgres scaffolds and import temporal-polyfill/global in the db template. - Composer 0.16.0 pins effect 4.0.0-rc.112 exactly; move the scaffold pin and overrides from rc.111 to match. Also make the e2e endpoint probe retry: the dev CLI emits the endpoint frame before the app process has bound its port, and the first request can race the seed, so a single fetch was flaky. The MIGRATION_PATH_NOT_FOUND failures seen while debugging were environmental, not a code bug: @prisma/dev persists database state per app name under ~/Library/Application Support/prisma-dev-nodejs/, so the always-named composer-app e2e database carried a schema marker stamped by an older toolchain that the rc.8 migration graph has no path from. A clean slate needs that directory removed along with ~/.prisma-composer/emulators. Signed-off-by: willbot Signed-off-by: Will Madden --- src/constants/dependencies.ts | 3 +++ src/tasks/install.ts | 1 + templates/create/_shared/src/prisma/db.ts.hbs | 2 ++ tests/e2e/create-prisma.e2e.test.ts | 16 +++++++++++++++- 4 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/constants/dependencies.ts b/src/constants/dependencies.ts index 8fae75d..df0d6fd 100644 --- a/src/constants/dependencies.ts +++ b/src/constants/dependencies.ts @@ -19,6 +19,9 @@ export const dependencyVersionMap = { "mongodb-memory-server": "^11.1.0", nitro: "^3.0.260610-beta", prisma: "8.0.0-rc.12", + // The ORM runtime's timestamp columns need a global Temporal, which no + // stable Node or Bun ships yet. + "temporal-polyfill": "^1.0.4", tsx: "^4.21.0", typescript: "^5.9.3", } as const; diff --git a/src/tasks/install.ts b/src/tasks/install.ts index 55f9821..10cb5b7 100644 --- a/src/tasks/install.ts +++ b/src/tasks/install.ts @@ -144,6 +144,7 @@ export async function writePrismaDependencies( projectDir = process.cwd(), ): Promise { const dependencies = [getDbPackages(provider)]; + if (provider === "postgres" && packageManager !== "deno") dependencies.push("temporal-polyfill"); if (provider === "mongo") dependencies.push("arktype", "mongodb"); if (packageManager === "deno") dependencies.push("dotenv"); diff --git a/templates/create/_shared/src/prisma/db.ts.hbs b/templates/create/_shared/src/prisma/db.ts.hbs index bd47963..26e0e9d 100644 --- a/templates/create/_shared/src/prisma/db.ts.hbs +++ b/templates/create/_shared/src/prisma/db.ts.hbs @@ -12,6 +12,8 @@ if (!databaseUrl) { export const db = postgres({ contractJson, url: databaseUrl }); {{else}} +import "temporal-polyfill/global"; + import service from "../../service.ts"; import type { Contract } from "./{{#if (eq authoring "typescript")}}generated/{{/if}}contract.d.ts"; import contractJson from "./{{#if (eq authoring "typescript")}}generated/{{/if}}contract.json" with { type: "json" }; diff --git a/tests/e2e/create-prisma.e2e.test.ts b/tests/e2e/create-prisma.e2e.test.ts index 759f7c0..8c3e635 100644 --- a/tests/e2e/create-prisma.e2e.test.ts +++ b/tests/e2e/create-prisma.e2e.test.ts @@ -84,6 +84,18 @@ function findAppEndpoint(output: string): string | undefined { } } +async function fetchUntilReady(url: string, deadline: number): Promise { + while (true) { + try { + const response = await fetch(url); + if (response.status === 200 || Date.now() >= deadline) return response; + } catch (error) { + if (Date.now() >= deadline) throw error; + } + await Bun.sleep(1_000); + } +} + async function verifyComposerDev(projectDir: string) { const process = Bun.spawn({ cmd: ["bun", "run", "dev:composer"], @@ -123,7 +135,9 @@ async function verifyComposerDev(projectDir: string) { const appUrl = findAppEndpoint(output); if (!appUrl) continue; - const response = await fetch(appUrl); + // The endpoint frame can precede the app process binding its port, so + // retry connection refusals until the deadline. + const response = await fetchUntilReady(appUrl, deadline); expect(response.status).toBe(200); const body = (await response.json()) as { users: Array<{ name: string }> }; expect(body.users.map((user) => user.name)).toEqual(["Alice", "Bob", "Carol"]);