diff --git a/src/tasks/setup-prisma.ts b/src/tasks/setup-prisma.ts index 9a8bcc7..132e2f4 100644 --- a/src/tasks/setup-prisma.ts +++ b/src/tasks/setup-prisma.ts @@ -77,7 +77,7 @@ const DEFAULT_PRISMA_POSTGRES = true; const DEFAULT_INSTALL = true; const DEFAULT_GENERATE = true; const DEFAULT_MIGRATE_AND_SEED = true; -const PRISMA_POSTGRES_MIGRATION_DELAY_MS = 2000; +const PRISMA_POSTGRES_MIGRATION_RETRY_DELAYS_MS = [1000, 2000, 4000]; const requiredPrismaFileGroups = [ ["prisma/schema.prisma", "packages/db/prisma/schema.prisma"], @@ -851,15 +851,21 @@ async function migrateAndSeedIfRequested( migrateSpinner.start("Creating and applying initial migration..."); let didMigrate = false; try { - if (context.shouldUsePrismaPostgres) { - // Newly provisioned Prisma Postgres databases can briefly reject the first migration. - // TODO(2026-04-26): replace this grace period with an explicit readiness probe. - await new Promise((resolve) => setTimeout(resolve, PRISMA_POSTGRES_MIGRATION_DELAY_MS)); + for (let attempt = 0; ; attempt++) { + try { + await execa(migrateInvocation.command, migrateInvocation.args, { + cwd: prismaProjectDir, + stdio: context.verbose ? "inherit" : "pipe", + }); + break; + } catch (error) { + const retryDelayMs = PRISMA_POSTGRES_MIGRATION_RETRY_DELAYS_MS[attempt]; + if (!context.shouldUsePrismaPostgres || retryDelayMs === undefined) { + throw error; + } + await new Promise((resolve) => setTimeout(resolve, retryDelayMs)); + } } - await execa(migrateInvocation.command, migrateInvocation.args, { - cwd: prismaProjectDir, - stdio: context.verbose ? "inherit" : "pipe", - }); migrateSpinner.stop("Initial migration applied."); didMigrate = true; } catch (error) { diff --git a/templates/create/astro/astro.config.mjs.hbs b/templates/create/astro/astro.config.mjs.hbs index 2b3edb2..513edc6 100644 --- a/templates/create/astro/astro.config.mjs.hbs +++ b/templates/create/astro/astro.config.mjs.hbs @@ -2,20 +2,23 @@ import { defineConfig } from "astro/config"; import node from "@astrojs/node"; +// Workaround for cookie CJS imports on Prisma Compute's Bun runtime; build-only because noExternal breaks `astro dev`. +/** @type {import("astro").AstroIntegration} */ +const bundleSsrDependenciesOnBuild = { + name: "bundle-ssr-dependencies-on-build", + hooks: { + "astro:config:setup": ({ command, updateConfig }) => { + if (command === "build") { + updateConfig({ vite: { ssr: { noExternal: true } } }); + } + }, + }, +}; + // https://astro.build/config export default defineConfig({ output: "server", adapter: node({ mode: "standalone" }), server: { host: true }, - vite: { - ssr: { - // Bundle SSR dependencies into the server entry. Astro's standalone - // build emits `import { parse, serialize } from "cookie"`, and the Bun - // runtime on Prisma Compute cannot resolve those named exports from - // cookie's CommonJS build at runtime (SyntaxError: Export named 'parse' - // not found). Bundling resolves the imports at build time. Remove once - // the Bun/cookie CJS named-export interop is fixed upstream. - noExternal: true, - }, - }, + integrations: [bundleSsrDependenciesOnBuild], }); diff --git a/templates/create/next/package.json.hbs b/templates/create/next/package.json.hbs index af77275..bd7e088 100644 --- a/templates/create/next/package.json.hbs +++ b/templates/create/next/package.json.hbs @@ -13,7 +13,7 @@ "lint": "eslint" }, "dependencies": { - "next": "16.2.9", + "next": "16.2.10", "react": "19.2.7", "react-dom": "19.2.7" }, @@ -22,7 +22,7 @@ "@types/react": "^19.2.17", "@types/react-dom": "^19.2.3", "eslint": "^10.5.0", - "eslint-config-next": "16.2.9", + "eslint-config-next": "16.2.10", "tsx": "^4.22.4", "typescript": "^6.0.3" } diff --git a/templates/create/turborepo/apps/api/package.json.hbs b/templates/create/turborepo/apps/api/package.json.hbs index e0f4cc3..746f57a 100644 --- a/templates/create/turborepo/apps/api/package.json.hbs +++ b/templates/create/turborepo/apps/api/package.json.hbs @@ -11,7 +11,7 @@ {{else}} "dev": "tsx watch src/index.ts", "build": "tsc", - "start": "node dist/src/index.js", + "start": "tsx src/index.ts", "typecheck": "tsc --noEmit" {{/if}} }, diff --git a/templates/create/turborepo/packages/db/src/client.ts.hbs b/templates/create/turborepo/packages/db/src/client.ts.hbs index a5388dd..2c7d153 100644 --- a/templates/create/turborepo/packages/db/src/client.ts.hbs +++ b/templates/create/turborepo/packages/db/src/client.ts.hbs @@ -1,6 +1,6 @@ -{{#unless (eq packageManager "deno")}} import path from "node:path"; import { fileURLToPath } from "node:url"; +{{#unless (eq packageManager "deno")}} import dotenv from "dotenv"; {{/unless}} import { PrismaClient } from "./generated/prisma/client{{#if (eq packageManager "deno")}}.ts{{/if}}"; @@ -20,14 +20,22 @@ import { {{sqliteAdapterClass packageManager}} } from "{{sqliteAdapterPackage pa import { PrismaMssql } from "@prisma/adapter-mssql"; {{/if}} -{{#unless (eq packageManager "deno")}} const moduleDir = path.dirname(fileURLToPath(import.meta.url)); +{{#unless (eq packageManager "deno")}} dotenv.config({ path: path.resolve(moduleDir, "../.env"), quiet: true }); {{/unless}} const rawDatabaseUrl = process.env.DATABASE_URL; {{#if (eq provider "sqlite")}} -const databaseUrl = (rawDatabaseUrl ?? "").trim() || "file:./dev.db"; +function resolveSqliteUrl(url: string): string { + const filePath = url.replace(/^file:(?:\/\/)?/, ""); + if (!url.startsWith("file:") || path.isAbsolute(filePath)) { + return url; + } + return `file:${path.resolve(moduleDir, "..", filePath)}`; +} + +const databaseUrl = resolveSqliteUrl((rawDatabaseUrl ?? "").trim() || "file:./dev.db"); {{else}} const databaseUrl = (rawDatabaseUrl ?? "").trim(); if (!databaseUrl) { diff --git a/templates/create/turborepo/turbo.json b/templates/create/turborepo/turbo.json index 8d0a230..ae1fd7a 100644 --- a/templates/create/turborepo/turbo.json +++ b/templates/create/turborepo/turbo.json @@ -1,5 +1,6 @@ { "$schema": "https://turbo.build/schema.json", + "globalPassThroughEnv": ["PORT", "DATABASE_URL"], "tasks": { "build": { "dependsOn": ["^build"],