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..df0d6fd 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,10 @@ 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", + // 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; @@ -29,7 +32,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/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/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/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/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..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"]); @@ -354,11 +368,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", () => {