create-prisma 0.4.1 → 0.4.2-next.37.104.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/README.md +66 -44
  2. package/dist/cli.mjs +1 -1
  3. package/dist/{create-H6Tk0JlE.mjs → create-BeFhatOc.mjs} +910 -993
  4. package/dist/index.d.mts +32 -39
  5. package/dist/index.mjs +3 -3
  6. package/package.json +7 -2
  7. package/templates/create/_shared/prisma/seed.ts.hbs +52 -0
  8. package/templates/create/astro/README.md.hbs +25 -15
  9. package/templates/create/astro/astro.config.mjs +6 -1
  10. package/templates/create/astro/deno.json.hbs +1 -1
  11. package/templates/create/astro/package.json.hbs +3 -2
  12. package/templates/create/astro/src/lib/prisma.ts.hbs +60 -40
  13. package/templates/create/astro/src/pages/api/users.ts.hbs +6 -22
  14. package/templates/create/astro/src/pages/index.astro.hbs +18 -55
  15. package/templates/create/elysia/README.md.hbs +27 -17
  16. package/templates/create/elysia/deno.json.hbs +1 -1
  17. package/templates/create/elysia/src/index.ts.hbs +15 -11
  18. package/templates/create/elysia/src/lib/prisma.ts.hbs +60 -43
  19. package/templates/create/elysia/tsconfig.json +1 -0
  20. package/templates/create/hono/README.md.hbs +27 -17
  21. package/templates/create/hono/deno.json.hbs +1 -1
  22. package/templates/create/hono/src/index.ts.hbs +9 -10
  23. package/templates/create/hono/src/lib/prisma.ts.hbs +60 -43
  24. package/templates/create/hono/tsconfig.json +1 -0
  25. package/templates/create/minimal/.yarnrc.yml.hbs +3 -0
  26. package/templates/create/minimal/README.md.hbs +39 -0
  27. package/templates/create/{turborepo → minimal}/deno.json.hbs +1 -1
  28. package/templates/create/minimal/package.json.hbs +13 -0
  29. package/templates/create/minimal/src/index.ts.hbs +43 -0
  30. package/templates/create/minimal/src/lib/prisma.ts.hbs +73 -0
  31. package/templates/create/{turborepo/apps/api → minimal}/tsconfig.json +3 -4
  32. package/templates/create/nest/README.md.hbs +28 -17
  33. package/templates/create/nest/deno.json.hbs +1 -1
  34. package/templates/create/nest/src/app.module.ts.hbs +5 -6
  35. package/templates/create/nest/src/lib/prisma.ts.hbs +60 -45
  36. package/templates/create/nest/src/prisma.service.ts.hbs +6 -5
  37. package/templates/create/nest/src/users.controller.ts.hbs +1 -2
  38. package/templates/create/nest/src/users.service.ts.hbs +2 -8
  39. package/templates/create/nest/tsconfig.json +1 -0
  40. package/templates/create/next/README.md.hbs +25 -13
  41. package/templates/create/next/deno.json.hbs +1 -1
  42. package/templates/create/next/src/app/page.tsx.hbs +16 -57
  43. package/templates/create/next/src/lib/prisma.ts.hbs +60 -40
  44. package/templates/create/next/tsconfig.json +1 -0
  45. package/templates/create/nuxt/README.md.hbs +26 -13
  46. package/templates/create/nuxt/app/pages/index.vue.hbs +12 -45
  47. package/templates/create/nuxt/deno.json.hbs +1 -1
  48. package/templates/create/nuxt/nuxt.config.ts +21 -0
  49. package/templates/create/nuxt/package.json.hbs +2 -1
  50. package/templates/create/nuxt/server/api/users.get.ts.hbs +4 -15
  51. package/templates/create/nuxt/server/utils/prisma.ts.hbs +60 -40
  52. package/templates/create/svelte/README.md.hbs +25 -15
  53. package/templates/create/svelte/deno.json.hbs +1 -1
  54. package/templates/create/svelte/package.json.hbs +1 -1
  55. package/templates/create/svelte/src/lib/server/prisma.ts.hbs +61 -41
  56. package/templates/create/svelte/src/routes/+page.server.ts.hbs +4 -15
  57. package/templates/create/svelte/src/routes/+page.svelte.hbs +13 -163
  58. package/templates/create/svelte/vite.config.ts +2 -1
  59. package/templates/create/tanstack-start/README.md.hbs +26 -13
  60. package/templates/create/tanstack-start/deno.json.hbs +1 -2
  61. package/templates/create/tanstack-start/package.json.hbs +1 -2
  62. package/templates/create/tanstack-start/src/lib/prisma.server.ts.hbs +60 -40
  63. package/templates/create/tanstack-start/src/routes/__root.tsx.hbs +2 -3
  64. package/templates/create/tanstack-start/src/routes/index.tsx.hbs +26 -77
  65. package/templates/create/tanstack-start/tsconfig.json +1 -0
  66. package/templates/create/tanstack-start/vite.config.ts +7 -1
  67. package/templates/create/astro/prisma/schema.prisma.hbs +0 -21
  68. package/templates/create/astro/prisma/seed.ts.hbs +0 -38
  69. package/templates/create/astro/prisma.config.ts +0 -13
  70. package/templates/create/elysia/prisma/schema.prisma.hbs +0 -25
  71. package/templates/create/elysia/prisma/seed.ts.hbs +0 -42
  72. package/templates/create/elysia/prisma.config.ts.hbs +0 -15
  73. package/templates/create/hono/prisma/schema.prisma.hbs +0 -25
  74. package/templates/create/hono/prisma/seed.ts.hbs +0 -42
  75. package/templates/create/hono/prisma.config.ts.hbs +0 -15
  76. package/templates/create/nest/prisma/schema.prisma.hbs +0 -25
  77. package/templates/create/nest/prisma/seed.ts.hbs +0 -44
  78. package/templates/create/nest/prisma.config.ts.hbs +0 -15
  79. package/templates/create/next/prisma/schema.prisma.hbs +0 -21
  80. package/templates/create/next/prisma/seed.ts.hbs +0 -38
  81. package/templates/create/next/prisma.config.ts +0 -13
  82. package/templates/create/nuxt/prisma/schema.prisma.hbs +0 -21
  83. package/templates/create/nuxt/prisma/seed.ts.hbs +0 -38
  84. package/templates/create/nuxt/prisma.config.ts +0 -13
  85. package/templates/create/svelte/prisma/schema.prisma.hbs +0 -21
  86. package/templates/create/svelte/prisma/seed.ts.hbs +0 -87
  87. package/templates/create/svelte/prisma.config.ts +0 -13
  88. package/templates/create/tanstack-start/prisma/schema.prisma.hbs +0 -21
  89. package/templates/create/tanstack-start/prisma/seed.ts.hbs +0 -37
  90. package/templates/create/tanstack-start/prisma.config.ts +0 -13
  91. package/templates/create/turborepo/README.md.hbs +0 -29
  92. package/templates/create/turborepo/apps/api/package.json.hbs +0 -19
  93. package/templates/create/turborepo/apps/api/src/index.ts.hbs +0 -51
  94. package/templates/create/turborepo/package.json.hbs +0 -24
  95. package/templates/create/turborepo/packages/db/package.json.hbs +0 -17
  96. package/templates/create/turborepo/packages/db/prisma/schema.prisma.hbs +0 -21
  97. package/templates/create/turborepo/packages/db/prisma/seed.ts.hbs +0 -38
  98. package/templates/create/turborepo/packages/db/prisma.config.ts +0 -13
  99. package/templates/create/turborepo/packages/db/src/client.ts.hbs +0 -58
  100. package/templates/create/turborepo/packages/db/src/index.ts +0 -2
  101. package/templates/create/turborepo/packages/db/tsconfig.json +0 -15
  102. package/templates/create/turborepo/turbo.json +0 -28
  103. /package/templates/create/{turborepo → elysia}/.yarnrc.yml.hbs +0 -0
@@ -1,15 +1,15 @@
1
1
  #!/usr/bin/env node
2
- import { cancel, confirm, intro, isCancel, log, multiselect, outro, select, spinner, text } from "@clack/prompts";
2
+ import { cancel, confirm, intro, isCancel, log, note, outro, select, spinner, text } from "@clack/prompts";
3
3
  import fs from "fs-extra";
4
4
  import path from "node:path";
5
+ import { randomUUID } from "node:crypto";
6
+ import os from "node:os";
7
+ import { PostHog } from "posthog-node";
5
8
  import Handlebars from "handlebars";
6
9
  import { existsSync } from "node:fs";
7
10
  import { fileURLToPath } from "node:url";
8
11
  import { z } from "zod";
9
12
  import { execa } from "execa";
10
- import { randomUUID } from "node:crypto";
11
- import os from "node:os";
12
- import { PostHog } from "posthog-node";
13
13
  import { styleText } from "node:util";
14
14
 
15
15
  //#region src/utils/runtime.ts
@@ -24,23 +24,60 @@ function requiresDotenvConfigImport(packageManager) {
24
24
  //#region src/constants/dependencies.ts
25
25
  const dependencyVersionMap = {
26
26
  "@elysiajs/node": "^1.4.5",
27
- "@prisma/client": "^7.4.0",
28
- "@prisma/adapter-pg": "^7.4.0",
29
- "@prisma/adapter-mariadb": "^7.4.0",
30
- "@prisma/adapter-better-sqlite3": "^7.4.0",
31
- "@prisma/adapter-mssql": "^7.4.0",
32
- "@types/node": "^24.3.0",
33
- dotenv: "^17.2.3",
34
- "node-gyp": "^11.5.0",
35
- prisma: "^7.4.0",
27
+ "@types/node": "^25.6.2",
28
+ dotenv: "^17.4.2",
29
+ "mongodb-memory-server": "^11.1.0",
36
30
  tsx: "^4.21.0"
37
31
  };
38
- function getWorkspaceDependencyVersion(packageManager) {
39
- return packageManager === "npm" ? "*" : "workspace:*";
32
+ const PRISMA_NEXT_DEFAULT_VERSION = "latest";
33
+ const PKG_PR_NEW_PREFIX = "pkg-pr-new:";
34
+ const PKG_PR_NEW_BASE_URL = "https://pkg.pr.new/prisma/prisma-next";
35
+ const DEFAULT_PRISMA_NEXT_SPEC = {
36
+ kind: "npm",
37
+ spec: PRISMA_NEXT_DEFAULT_VERSION
38
+ };
39
+ function parsePrismaNextVersionSpec(input) {
40
+ if (input === void 0) return DEFAULT_PRISMA_NEXT_SPEC;
41
+ const trimmed = input.trim();
42
+ if (trimmed.length === 0) return DEFAULT_PRISMA_NEXT_SPEC;
43
+ if (trimmed.startsWith(PKG_PR_NEW_PREFIX)) {
44
+ const ref = trimmed.slice(11).trim();
45
+ if (ref.length === 0) throw new Error(`Invalid --prisma-next-version value: '${input}'. Expected 'pkg-pr-new:<sha|branch|pr-number>'.`);
46
+ return {
47
+ kind: "pkg-pr-new",
48
+ ref
49
+ };
50
+ }
51
+ return {
52
+ kind: "npm",
53
+ spec: trimmed
54
+ };
55
+ }
56
+ function isPrismaNextPackage(packageName) {
57
+ return packageName === "prisma-next" || packageName.startsWith("@prisma-next/");
58
+ }
59
+ function getPrismaNextPackageSpecifier(packageName, spec = DEFAULT_PRISMA_NEXT_SPEC) {
60
+ if (spec.kind === "pkg-pr-new") return `${PKG_PR_NEW_BASE_URL}/${packageName}@${spec.ref}`;
61
+ return `${packageName}@${spec.spec}`;
62
+ }
63
+ function getDependencyVersion(packageName, prismaNextSpec = DEFAULT_PRISMA_NEXT_SPEC) {
64
+ if (isPrismaNextPackage(packageName)) {
65
+ if (prismaNextSpec.kind === "pkg-pr-new") return `${PKG_PR_NEW_BASE_URL}/${packageName}@${prismaNextSpec.ref}`;
66
+ return prismaNextSpec.spec;
67
+ }
68
+ return dependencyVersionMap[packageName];
69
+ }
70
+ function usesViteDevServer(template) {
71
+ return template === "astro" || template === "nuxt" || template === "svelte" || template === "tanstack-start";
40
72
  }
41
73
  function getCreateTemplateDependencies(template, packageManager) {
42
74
  const targets = [];
43
- if (template === "hono" || template === "elysia" || template === "nest") {
75
+ if (usesViteDevServer(template)) targets.push({
76
+ packageJsonPath: "package.json",
77
+ dependencies: [],
78
+ devDependencies: ["@prisma-next/vite-plugin-contract-emit"]
79
+ });
80
+ if (template === "minimal" || template === "hono" || template === "elysia" || template === "nest") {
44
81
  const runtimeDevDependencies = usesNodeStyleRuntime(packageManager) ? ["tsx"] : [];
45
82
  if (template === "elysia" && packageManager !== "deno") targets.push({
46
83
  packageJsonPath: "package.json",
@@ -53,23 +90,155 @@ function getCreateTemplateDependencies(template, packageManager) {
53
90
  devDependencies: runtimeDevDependencies
54
91
  });
55
92
  }
56
- if (template === "turborepo") targets.push({
57
- packageJsonPath: "apps/api/package.json",
58
- dependencies: [],
59
- devDependencies: ["tsx"],
60
- customDependencies: { "@repo/db": getWorkspaceDependencyVersion(packageManager) }
61
- });
62
93
  return targets;
63
94
  }
64
95
 
96
+ //#endregion
97
+ //#region src/telemetry/client.ts
98
+ const TELEMETRY_API_KEY = "phc_cmc85avbWyuJ2JyKdGPdv7dxXli8xLdWDBPbvIXWJfs";
99
+ const TELEMETRY_HOST = "https://us.i.posthog.com";
100
+ const TELEMETRY_CONFIG_FILE = "telemetry.json";
101
+ const UUID_V4_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
102
+ function isTruthyEnvValue(value) {
103
+ return [
104
+ "1",
105
+ "true",
106
+ "yes",
107
+ "on"
108
+ ].includes(String(value ?? "").trim().toLowerCase());
109
+ }
110
+ function shouldDisableTelemetry() {
111
+ if (isTruthyEnvValue(process.env.CI) || isTruthyEnvValue(process.env.GITHUB_ACTIONS)) return true;
112
+ return process.env.CREATE_PRISMA_DISABLE_TELEMETRY !== void 0 || process.env.CREATE_PRISMA_TELEMETRY_DISABLED !== void 0 || process.env.DO_NOT_TRACK !== void 0;
113
+ }
114
+ function getTelemetryConfigDir() {
115
+ if (process.platform === "darwin") return path.join(os.homedir(), "Library", "Application Support", "create-prisma");
116
+ if (process.platform === "win32") return path.join(process.env.APPDATA ?? path.join(os.homedir(), "AppData", "Roaming"), "create-prisma");
117
+ return path.join(process.env.XDG_CONFIG_HOME ?? path.join(os.homedir(), ".config"), "create-prisma");
118
+ }
119
+ async function getAnonymousId() {
120
+ const telemetryConfigPath = path.join(getTelemetryConfigDir(), TELEMETRY_CONFIG_FILE);
121
+ try {
122
+ const config = await fs.readJSON(telemetryConfigPath);
123
+ if (typeof config.anonymousId === "string" && UUID_V4_REGEX.test(config.anonymousId)) return config.anonymousId;
124
+ } catch {}
125
+ const anonymousId = randomUUID();
126
+ try {
127
+ await fs.ensureDir(path.dirname(telemetryConfigPath));
128
+ await fs.writeJSON(telemetryConfigPath, { anonymousId }, { spaces: 2 });
129
+ } catch {}
130
+ return anonymousId;
131
+ }
132
+ function getCommonProperties() {
133
+ return {
134
+ "cli-version": "0.4.2-next.37.104.1",
135
+ "node-version": process.version,
136
+ platform: process.platform,
137
+ arch: process.arch
138
+ };
139
+ }
140
+ function sanitizeProperties(properties) {
141
+ return Object.fromEntries(Object.entries(properties).filter(([, value]) => value !== void 0));
142
+ }
143
+ async function trackCliTelemetry(event, properties) {
144
+ if (shouldDisableTelemetry()) return;
145
+ let client;
146
+ try {
147
+ const distinctId = await getAnonymousId();
148
+ const sanitizedProperties = sanitizeProperties({
149
+ ...getCommonProperties(),
150
+ ...properties,
151
+ $process_person_profile: false
152
+ });
153
+ client = new PostHog(TELEMETRY_API_KEY, {
154
+ host: TELEMETRY_HOST,
155
+ disableGeoip: true,
156
+ flushAt: 1,
157
+ flushInterval: 0
158
+ });
159
+ await client.captureImmediate({
160
+ distinctId,
161
+ event,
162
+ properties: sanitizedProperties,
163
+ disableGeoip: true
164
+ });
165
+ } catch {} finally {
166
+ if (client) await client.shutdown().catch(() => {});
167
+ }
168
+ }
169
+
170
+ //#endregion
171
+ //#region src/telemetry/create.ts
172
+ function classifyPrismaNextSpec(spec) {
173
+ if (!spec || spec === DEFAULT_PRISMA_NEXT_SPEC) return "default";
174
+ if (spec.kind === "pkg-pr-new") return "pkg-pr-new";
175
+ if (spec.spec === PRISMA_NEXT_DEFAULT_VERSION) return "default";
176
+ return /^[0-9]/.test(spec.spec) ? "npm-version" : "npm-tag";
177
+ }
178
+ function getPrismaNextVersionSpecString(spec) {
179
+ if (!spec) return null;
180
+ return spec.kind === "pkg-pr-new" ? `pkg-pr-new:${spec.ref}` : spec.spec;
181
+ }
182
+ const CREATE_PRISMA_NEXT_COMPLETED_EVENT = "cli:create_prisma_next_command_completed";
183
+ const CREATE_PRISMA_NEXT_FAILED_EVENT = "cli:create_prisma_next_command_failed";
184
+ function getTargetDirectoryState(context) {
185
+ if (!context.targetPathState.exists) return "new";
186
+ if (context.targetPathState.isEmptyDirectory) return "empty_directory";
187
+ return "non_empty_directory";
188
+ }
189
+ function getBaseCreateProperties(input, context) {
190
+ const resolvedPrismaNextSpec = context?.prismaSetupContext.prismaNextSpec;
191
+ return {
192
+ command: "create",
193
+ "uses-defaults": input.yes === true,
194
+ verbose: input.verbose === true,
195
+ force: input.force === true,
196
+ template: context?.template ?? input.template ?? null,
197
+ "database-provider": context?.prismaSetupContext.databaseProvider ?? input.provider ?? null,
198
+ "authoring-style": context?.prismaSetupContext.authoring ?? input.authoring ?? null,
199
+ "package-manager": context?.prismaSetupContext.packageManager ?? input.packageManager ?? null,
200
+ "should-install": context?.prismaSetupContext.shouldInstall ?? input.install ?? null,
201
+ "should-emit": context?.prismaSetupContext.shouldEmit ?? input.emit ?? null,
202
+ "uses-prisma-postgres": context?.prismaSetupContext.shouldUsePrismaPostgres ?? input.prismaPostgres ?? null,
203
+ "target-directory-state": context ? getTargetDirectoryState(context) : null,
204
+ "prisma-next-version-kind": classifyPrismaNextSpec(resolvedPrismaNextSpec),
205
+ "prisma-next-version-spec": getPrismaNextVersionSpecString(resolvedPrismaNextSpec) ?? input.prismaNextVersion ?? null
206
+ };
207
+ }
208
+ function getErrorName(error) {
209
+ if (error instanceof Error) return error.name;
210
+ return error === void 0 ? null : "UnknownError";
211
+ }
212
+ function getErrorCode(error) {
213
+ if (typeof error !== "object" || error === null) return null;
214
+ const exitCode = Reflect.get(error, "exitCode");
215
+ if (typeof exitCode === "number") return exitCode;
216
+ const code = Reflect.get(error, "code");
217
+ return typeof code === "number" || typeof code === "string" ? code : null;
218
+ }
219
+ async function trackCreateCompleted(params) {
220
+ await trackCliTelemetry(CREATE_PRISMA_NEXT_COMPLETED_EVENT, {
221
+ ...getBaseCreateProperties(params.input, params.context),
222
+ "duration-ms": params.durationMs
223
+ });
224
+ }
225
+ async function trackCreateFailed(params) {
226
+ await trackCliTelemetry(CREATE_PRISMA_NEXT_FAILED_EVENT, {
227
+ ...getBaseCreateProperties(params.input, params.context),
228
+ "duration-ms": params.durationMs,
229
+ "failure-stage": params.stage,
230
+ "error-name": getErrorName(params.error),
231
+ "error-code": getErrorCode(params.error)
232
+ });
233
+ }
234
+
65
235
  //#endregion
66
236
  //#region src/types.ts
67
- const databaseProviders = [
237
+ const databaseProviderInputs = [
238
+ "postgres",
68
239
  "postgresql",
69
- "mysql",
70
- "sqlite",
71
- "sqlserver",
72
- "cockroachdb"
240
+ "mongo",
241
+ "mongodb"
73
242
  ];
74
243
  const packageManagers = [
75
244
  "npm",
@@ -78,8 +247,9 @@ const packageManagers = [
78
247
  "bun",
79
248
  "deno"
80
249
  ];
81
- const schemaPresets = ["empty", "basic"];
250
+ const authoringStyles = ["psl", "typescript"];
82
251
  const createTemplates = [
252
+ "minimal",
83
253
  "hono",
84
254
  "elysia",
85
255
  "nest",
@@ -87,56 +257,36 @@ const createTemplates = [
87
257
  "svelte",
88
258
  "astro",
89
259
  "nuxt",
90
- "tanstack-start",
91
- "turborepo"
260
+ "tanstack-start"
92
261
  ];
93
- const createAddons = [
94
- "skills",
95
- "mcp",
96
- "extension"
97
- ];
98
- const addonInstallScopes = ["project", "global"];
99
- const extensionTargets = [
100
- "vscode",
101
- "cursor",
102
- "windsurf"
103
- ];
104
- const prismaSkillNames = [
105
- "prisma-cli",
106
- "prisma-client-api",
107
- "prisma-database-setup",
108
- "prisma-upgrade-v7",
109
- "prisma-postgres"
110
- ];
111
- const DatabaseProviderSchema = z.enum(databaseProviders);
262
+ function normalizeDatabaseProvider(value) {
263
+ if (value === "postgresql") return "postgres";
264
+ if (value === "mongodb") return "mongo";
265
+ return value;
266
+ }
267
+ const DatabaseProviderSchema = z.enum(databaseProviderInputs).transform(normalizeDatabaseProvider);
112
268
  const PackageManagerSchema = z.enum(packageManagers);
113
- const SchemaPresetSchema = z.enum(schemaPresets);
269
+ const AuthoringStyleSchema = z.enum(authoringStyles);
114
270
  const CreateTemplateSchema = z.enum(createTemplates);
115
- const CreateAddonSchema = z.enum(createAddons);
116
- const AddonInstallScopeSchema = z.enum(addonInstallScopes);
117
- const ExtensionTargetSchema = z.enum(extensionTargets);
118
- const PrismaSkillNameSchema = z.enum(prismaSkillNames);
119
271
  const DatabaseUrlSchema = z.string().trim().min(1, "Please enter a valid database URL");
120
272
  const CommonCommandOptionsSchema = z.object({
121
273
  yes: z.boolean().optional().describe("Skip prompts and accept default choices"),
122
274
  verbose: z.boolean().optional().describe("Show verbose command output during setup")
123
275
  });
124
276
  const PrismaSetupOptionsSchema = z.object({
125
- provider: DatabaseProviderSchema.optional().describe("Database provider"),
277
+ provider: DatabaseProviderSchema.optional().describe("Prisma Next database target: PostgreSQL relational models or MongoDB document models"),
278
+ authoring: AuthoringStyleSchema.optional().describe("Contract authoring style"),
126
279
  packageManager: PackageManagerSchema.optional().describe("Package manager used for dependency installation"),
127
- prismaPostgres: z.boolean().optional().describe("Provision Prisma Postgres with create-db when provider is postgresql"),
280
+ prismaPostgres: z.boolean().optional().describe("Provision Prisma Postgres with create-db when target is postgres"),
128
281
  databaseUrl: DatabaseUrlSchema.optional().describe("DATABASE_URL value"),
129
282
  install: z.boolean().optional().describe("Install dependencies with selected package manager"),
130
- generate: z.boolean().optional().describe("Generate Prisma Client after scaffolding"),
131
- schemaPreset: SchemaPresetSchema.optional().describe("Schema preset to scaffold in prisma/schema.prisma")
283
+ emit: z.boolean().optional().describe("Emit Prisma Next contract artifacts after scaffolding"),
284
+ prismaNextVersion: z.string().trim().min(1).optional().describe("Prisma Next version, npm dist-tag, or 'pkg-pr-new:<sha|branch|pr>' (default: latest)")
132
285
  });
133
286
  const PrismaSetupCommandInputSchema = CommonCommandOptionsSchema.extend(PrismaSetupOptionsSchema.shape);
134
287
  const CreateScaffoldOptionsSchema = z.object({
135
288
  name: z.string().trim().min(1, "Please enter a valid project name").optional().describe("Project name / directory"),
136
289
  template: CreateTemplateSchema.optional().describe("Project template"),
137
- skills: z.boolean().optional().describe("Enable skills addon"),
138
- mcp: z.boolean().optional().describe("Enable MCP addon"),
139
- extension: z.boolean().optional().describe("Enable extension addon"),
140
290
  force: z.boolean().optional().describe("Allow scaffolding into a non-empty target directory")
141
291
  });
142
292
  const CreateCommandInputSchema = PrismaSetupCommandInputSchema.extend(CreateScaffoldOptionsSchema.shape);
@@ -221,13 +371,17 @@ function getPackageManagerManifestValue(packageManager) {
221
371
  return packageManagerManifestValues[packageManager];
222
372
  }
223
373
  function getDenoPrismaSpecifier() {
224
- return `npm:prisma@${dependencyVersionMap.prisma}`;
374
+ return "npm:prisma-next";
375
+ }
376
+ function getDenoNpmSpecifier(packageSpecifier) {
377
+ return `npm:${packageSpecifier.replace(/@latest$/, "")}`;
225
378
  }
226
379
  function getDenoAllowedScriptSpecifiers() {
227
380
  return [
228
- `npm:prisma@${dependencyVersionMap.prisma}`,
229
- `npm:@prisma/client@${dependencyVersionMap["@prisma/client"]}`,
230
- `npm:@prisma/engines@${dependencyVersionMap.prisma}`
381
+ "npm:prisma-next",
382
+ "npm:@prisma-next/postgres",
383
+ "npm:@prisma-next/mongo",
384
+ "npm:mongodb-memory-server"
231
385
  ].join(",");
232
386
  }
233
387
  function getInstallCommand(packageManager) {
@@ -311,7 +465,7 @@ function getPackageExecutionArgs(packageManager, commandArgs) {
311
465
  args: [
312
466
  "run",
313
467
  "-A",
314
- `npm:${packageName}`,
468
+ getDenoNpmSpecifier(packageName),
315
469
  ...args
316
470
  ]
317
471
  };
@@ -326,26 +480,46 @@ function getPackageExecutionCommand(packageManager, commandArgs) {
326
480
  const execution = getPackageExecutionArgs(packageManager, commandArgs);
327
481
  return [execution.command, ...execution.args].join(" ");
328
482
  }
329
- function getPrismaCliArgs(packageManager, prismaArgs) {
330
- if (packageManager === "bun") return getPackageExecutionArgs(packageManager, [
331
- "--bun",
332
- "prisma",
333
- ...prismaArgs
334
- ]);
335
- if (packageManager === "deno") return {
336
- command: "deno",
337
- args: [
338
- "run",
339
- "-A",
340
- "--env-file=.env",
341
- getDenoPrismaSpecifier(),
342
- ...prismaArgs
343
- ]
344
- };
345
- return getPackageExecutionArgs(packageManager, ["prisma", ...prismaArgs]);
483
+ function getLocalPackageBinaryArgs(packageManager, binaryName, binaryArgs) {
484
+ switch (packageManager) {
485
+ case "pnpm": return {
486
+ command: "pnpm",
487
+ args: [
488
+ "exec",
489
+ binaryName,
490
+ ...binaryArgs
491
+ ]
492
+ };
493
+ case "yarn": return {
494
+ command: "yarn",
495
+ args: [binaryName, ...binaryArgs]
496
+ };
497
+ case "bun": return {
498
+ command: "bun",
499
+ args: [binaryName, ...binaryArgs]
500
+ };
501
+ case "deno": return {
502
+ command: "deno",
503
+ args: [
504
+ "run",
505
+ "-A",
506
+ `npm:${binaryName}`,
507
+ ...binaryArgs
508
+ ]
509
+ };
510
+ default: return {
511
+ command: "npm",
512
+ args: [
513
+ "exec",
514
+ binaryName,
515
+ "--",
516
+ ...binaryArgs
517
+ ]
518
+ };
519
+ }
346
520
  }
347
- function getPrismaCliCommand(packageManager, prismaArgs) {
348
- const execution = getPrismaCliArgs(packageManager, prismaArgs);
521
+ function getLocalPackageBinaryCommand(packageManager, binaryName, binaryArgs) {
522
+ const execution = getLocalPackageBinaryArgs(packageManager, binaryName, binaryArgs);
349
523
  return [execution.command, ...execution.args].join(" ");
350
524
  }
351
525
 
@@ -431,97 +605,133 @@ async function renderTemplateTree(opts) {
431
605
  function getCreateTemplateDir(template) {
432
606
  return resolveTemplatesDir(`templates/create/${template}`);
433
607
  }
434
- function createTemplateContext(projectName, provider, schemaPreset, packageManager) {
608
+ function getCreateSharedTemplateDir() {
609
+ return resolveTemplatesDir("templates/create/_shared");
610
+ }
611
+ function createTemplateContext(projectName, template, provider, authoring, packageManager) {
435
612
  return {
436
613
  projectName,
614
+ template,
437
615
  provider,
438
- schemaPreset,
616
+ authoring,
439
617
  packageManager
440
618
  };
441
619
  }
442
620
  async function scaffoldCreateTemplate(opts) {
443
- const { projectDir, projectName, template, provider, schemaPreset, packageManager } = opts;
621
+ const { projectDir, projectName, template, provider, authoring, packageManager } = opts;
622
+ const templateRoot = getCreateTemplateDir(template);
623
+ const sharedTemplateRoot = getCreateSharedTemplateDir();
624
+ const context = createTemplateContext(projectName, template, provider, authoring, packageManager);
625
+ await renderTemplateTree({
626
+ templateRoot: sharedTemplateRoot,
627
+ outputDir: projectDir,
628
+ context
629
+ });
444
630
  await renderTemplateTree({
445
- templateRoot: getCreateTemplateDir(template),
631
+ templateRoot,
446
632
  outputDir: projectDir,
447
- context: createTemplateContext(projectName, provider, schemaPreset, packageManager)
633
+ context
448
634
  });
449
635
  }
450
636
 
451
637
  //#endregion
452
638
  //#region src/constants/db-packages.ts
453
- function getDbPackages(provider) {
639
+ function getDbPackages(provider, _packageManager) {
454
640
  switch (provider) {
455
- case "postgresql":
456
- case "cockroachdb": return "@prisma/adapter-pg";
457
- case "mysql": return "@prisma/adapter-mariadb";
458
- case "sqlite": return "@prisma/adapter-better-sqlite3";
459
- case "sqlserver": return "@prisma/adapter-mssql";
641
+ case "postgres": return "@prisma-next/postgres";
642
+ case "mongo": return "@prisma-next/mongo";
460
643
  default: {
461
644
  const exhaustiveCheck = provider;
462
- throw new Error(`Unsupported database provider: ${String(exhaustiveCheck)}`);
645
+ throw new Error(`Unsupported Prisma Next target: ${String(exhaustiveCheck)}`);
463
646
  }
464
647
  }
465
648
  }
466
649
 
467
650
  //#endregion
468
651
  //#region src/tasks/install.ts
469
- function getPrismaScriptMap(packageManager) {
652
+ function getPrismaNextScriptMap(packageManager) {
470
653
  if (packageManager === "deno") {
471
- const prismaSpecifier = getDenoPrismaSpecifier();
654
+ const prismaNextCli = `deno run -A --env-file=.env ${getDenoPrismaSpecifier()}`;
472
655
  return {
473
- "db:generate": `deno run -A --env-file=.env ${prismaSpecifier} generate`,
474
- "db:push": `deno run -A --env-file=.env ${prismaSpecifier} db push`,
475
- "db:migrate": `deno run -A --env-file=.env ${prismaSpecifier} migrate dev`,
476
- "db:seed": `deno run -A --env-file=.env ${prismaSpecifier} db seed`
656
+ "contract:emit": `${prismaNextCli} contract emit`,
657
+ "db:init": `${prismaNextCli} db init`,
658
+ "db:update": `${prismaNextCli} db update`,
659
+ "db:verify": `${prismaNextCli} db verify`,
660
+ "db:seed": "deno run -A --env-file=.env prisma/seed.ts",
661
+ "migration:plan": `${prismaNextCli} migration plan`,
662
+ migrate: `${prismaNextCli} migrate`,
663
+ "migration:status": `${prismaNextCli} migration status`,
664
+ "migration:show": `${prismaNextCli} migration show`
477
665
  };
478
666
  }
479
667
  if (packageManager === "bun") {
480
- const prismaCli = "bun --env-file=.env ./node_modules/.bin/prisma";
668
+ const prismaNextCli = "bun prisma-next";
481
669
  return {
482
- "db:generate": `${prismaCli} generate`,
483
- "db:push": `${prismaCli} db push`,
484
- "db:migrate": `${prismaCli} migrate dev`,
485
- "db:seed": `${prismaCli} db seed`
670
+ "contract:emit": `${prismaNextCli} contract emit`,
671
+ "db:init": `${prismaNextCli} db init`,
672
+ "db:update": `${prismaNextCli} db update`,
673
+ "db:verify": `${prismaNextCli} db verify`,
674
+ "db:seed": "bun prisma/seed.ts",
675
+ "migration:plan": `${prismaNextCli} migration plan`,
676
+ migrate: `${prismaNextCli} migrate`,
677
+ "migration:status": `${prismaNextCli} migration status`,
678
+ "migration:show": `${prismaNextCli} migration show`
486
679
  };
487
680
  }
488
681
  return {
489
- "db:generate": "prisma generate",
490
- "db:push": "prisma db push",
491
- "db:migrate": "prisma migrate dev",
492
- "db:seed": "prisma db seed"
682
+ "contract:emit": "prisma-next contract emit",
683
+ "db:init": "prisma-next db init",
684
+ "db:update": "prisma-next db update",
685
+ "db:verify": "prisma-next db verify",
686
+ "db:seed": "tsx prisma/seed.ts",
687
+ "migration:plan": "prisma-next migration plan",
688
+ migrate: "prisma-next migrate",
689
+ "migration:status": "prisma-next migration status",
690
+ "migration:show": "prisma-next migration show"
493
691
  };
494
692
  }
495
- function getVersion(packageName) {
496
- return dependencyVersionMap[packageName];
497
- }
498
693
  function unique(items) {
499
694
  return [...new Set(items)];
500
695
  }
501
696
  function sortRecord(record) {
502
697
  return Object.fromEntries(Object.entries(record).sort(([a], [b]) => a.localeCompare(b)));
503
698
  }
504
- async function projectContainsText(projectDir, text) {
505
- const directories = [projectDir];
506
- while (directories.length > 0) {
507
- const currentDirectory = directories.pop();
508
- if (!currentDirectory) continue;
509
- const entries = await fs.readdir(currentDirectory, { withFileTypes: true });
510
- for (const entry of entries) {
511
- if (entry.name === "node_modules" || entry.name === ".git") continue;
512
- const entryPath = path.join(currentDirectory, entry.name);
513
- if (entry.isDirectory()) {
514
- directories.push(entryPath);
515
- continue;
516
- }
517
- if (!entry.isFile() || !/\.(c|m)?[jt]sx?$/.test(entry.name)) continue;
518
- if ((await fs.readFile(entryPath, "utf8")).includes(text)) return true;
519
- }
520
- }
521
- return false;
699
+ function getGeneratedContractTypePackages(provider) {
700
+ if (provider === "mongo") return [
701
+ "@prisma-next/adapter-mongo",
702
+ "@prisma-next/contract",
703
+ "@prisma-next/mongo-contract"
704
+ ];
705
+ return [
706
+ "@prisma-next/adapter-postgres",
707
+ "@prisma-next/contract",
708
+ "@prisma-next/sql-contract",
709
+ "@prisma-next/target-postgres"
710
+ ];
711
+ }
712
+ function getTypeScriptContractPackages(provider) {
713
+ if (provider === "mongo") return [
714
+ ...getGeneratedContractTypePackages(provider),
715
+ "@prisma-next/family-mongo",
716
+ "@prisma-next/mongo-contract-ts",
717
+ "@prisma-next/target-mongo"
718
+ ];
719
+ return [
720
+ ...getGeneratedContractTypePackages(provider),
721
+ "@prisma-next/family-sql",
722
+ "@prisma-next/sql-contract-ts"
723
+ ];
724
+ }
725
+ function getMigrationPackages(provider) {
726
+ if (provider === "mongo") return ["@prisma-next/family-mongo", "@prisma-next/target-mongo"];
727
+ return ["@prisma-next/target-postgres"];
728
+ }
729
+ function getOrmTypePackages(provider) {
730
+ if (provider === "mongo") return ["@prisma-next/mongo-orm"];
731
+ return ["@prisma-next/sql-orm-client"];
522
732
  }
523
733
  async function addPackageDependency(opts) {
524
- const { dependencies = [], devDependencies = [], customDependencies = {}, scripts = {}, scriptMode, projectDir } = opts;
734
+ const { dependencies = [], devDependencies = [], customDependencies = {}, scripts = {}, scriptMode, projectDir, prismaNextSpec = DEFAULT_PRISMA_NEXT_SPEC } = opts;
525
735
  const pkgJsonPath = path.join(projectDir, "package.json");
526
736
  if (!await fs.pathExists(pkgJsonPath)) throw new Error(`No package.json found in ${projectDir}. Run this command inside an existing JavaScript/TypeScript project.`);
527
737
  const pkgJson = await fs.readJson(pkgJsonPath);
@@ -529,12 +739,12 @@ async function addPackageDependency(opts) {
529
739
  if (!pkgJson.devDependencies) pkgJson.devDependencies = {};
530
740
  if (!pkgJson.scripts) pkgJson.scripts = {};
531
741
  for (const pkgName of unique(dependencies)) {
532
- const version = getVersion(pkgName);
742
+ const version = getDependencyVersion(pkgName, prismaNextSpec);
533
743
  if (version) pkgJson.dependencies[pkgName] = version;
534
744
  else console.warn(`Warning: Dependency ${pkgName} not found in version map.`);
535
745
  }
536
746
  for (const pkgName of unique(devDependencies)) {
537
- const version = getVersion(pkgName);
747
+ const version = getDependencyVersion(pkgName, prismaNextSpec);
538
748
  if (version) pkgJson.devDependencies[pkgName] = version;
539
749
  else console.warn(`Warning: Dev dependency ${pkgName} not found in version map.`);
540
750
  }
@@ -550,22 +760,27 @@ async function addPackageDependency(opts) {
550
760
  pkgJson.devDependencies = sortRecord(pkgJson.devDependencies);
551
761
  await fs.writeJson(pkgJsonPath, pkgJson, { spaces: 2 });
552
762
  }
553
- async function writePrismaDependencies(provider, packageManager, projectDir = process.cwd()) {
554
- const dependencies = ["@prisma/client"];
555
- const devDependencies = ["prisma"];
556
- dependencies.push(getDbPackages(provider));
557
- if (requiresDotenvConfigImport(packageManager) || await projectContainsText(projectDir, "dotenv/config")) dependencies.push("dotenv");
558
- if (provider === "sqlite" && packageManager === "deno") devDependencies.push("node-gyp");
763
+ async function writePrismaDependencies(provider, packageManager, authoring, projectDir = process.cwd(), prismaNextSpec = DEFAULT_PRISMA_NEXT_SPEC) {
764
+ const dependencies = [getDbPackages(provider, packageManager), "dotenv"];
765
+ const devDependencies = [
766
+ "prisma-next",
767
+ "@prisma-next/cli",
768
+ "@types/node"
769
+ ];
770
+ devDependencies.push(...getGeneratedContractTypePackages(provider));
771
+ devDependencies.push(...getMigrationPackages(provider));
772
+ devDependencies.push(...getOrmTypePackages(provider));
773
+ if (authoring === "typescript") devDependencies.push(...getTypeScriptContractPackages(provider));
559
774
  await addPackageDependency({
560
775
  dependencies,
561
776
  devDependencies,
562
- scripts: getPrismaScriptMap(packageManager),
563
- scriptMode: "if-missing",
564
- projectDir
777
+ scripts: getPrismaNextScriptMap(packageManager),
778
+ projectDir,
779
+ prismaNextSpec
565
780
  });
566
781
  }
567
782
  async function writeCreateTemplateDependencies(opts) {
568
- const { template, packageManager, projectDir = process.cwd() } = opts;
783
+ const { template, packageManager, projectDir = process.cwd(), prismaNextSpec = DEFAULT_PRISMA_NEXT_SPEC } = opts;
569
784
  const targets = getCreateTemplateDependencies(template, packageManager);
570
785
  for (const dependencyTarget of targets) {
571
786
  const targetDirectory = path.join(projectDir, path.dirname(dependencyTarget.packageJsonPath));
@@ -573,15 +788,18 @@ async function writeCreateTemplateDependencies(opts) {
573
788
  dependencies: dependencyTarget.dependencies,
574
789
  devDependencies: dependencyTarget.devDependencies,
575
790
  customDependencies: dependencyTarget.customDependencies,
576
- projectDir: targetDirectory
791
+ projectDir: targetDirectory,
792
+ prismaNextSpec
577
793
  });
578
794
  }
579
795
  }
580
796
  async function installProjectDependencies(packageManager, projectDir = process.cwd(), options = {}) {
581
797
  const verbose = options.verbose === true;
582
798
  const installCommand = getInstallArgs(packageManager);
799
+ const env = packageManager === "yarn" ? { YARN_ENABLE_IMMUTABLE_INSTALLS: "false" } : void 0;
583
800
  await execa(installCommand.command, installCommand.args, {
584
801
  cwd: projectDir,
802
+ env,
585
803
  stdio: verbose ? "inherit" : "pipe"
586
804
  });
587
805
  }
@@ -643,55 +861,233 @@ function getCreateDbCommand(packageManager) {
643
861
 
644
862
  //#endregion
645
863
  //#region src/tasks/setup-prisma.ts
646
- const DEFAULT_DATABASE_PROVIDER = "postgresql";
647
- const DEFAULT_SCHEMA_PRESET$1 = "empty";
648
- const DEFAULT_PRISMA_POSTGRES = true;
864
+ const DEFAULT_DATABASE_PROVIDER = "postgres";
865
+ const DEFAULT_AUTHORING = "psl";
649
866
  const DEFAULT_INSTALL = true;
650
- const DEFAULT_GENERATE = true;
867
+ const DEFAULT_EMIT = true;
868
+ const DEFAULT_INTERACTIVE_PRISMA_POSTGRES = true;
869
+ const DEFAULT_AUTOMATED_PRISMA_POSTGRES = false;
870
+ const MONGO_MEMORY_SERVER_SCRIPT = `import { spawn } from "node:child_process";
871
+ import { closeSync, existsSync, mkdirSync, openSync, readFileSync, rmSync, writeFileSync } from "node:fs";
872
+ import path from "node:path";
873
+
874
+ const defaultDatabaseUrl = "mongodb://localhost:27017/mydb?replicaSet=rs0&directConnection=true";
875
+ const dataRoot = path.resolve(process.env.MONGO_DB_PATH ?? ".mongo-data");
876
+ const dbPath = path.join(dataRoot, "db");
877
+ const pidFile = path.join(dataRoot, "mongo.pid");
878
+ const logFile = path.join(dataRoot, "mongo.log");
879
+ const readyTimeoutMs = Number(process.env.MONGO_READY_TIMEOUT_MS ?? 60_000);
880
+
881
+ function getMongoConfig() {
882
+ const databaseUrl = process.env.DATABASE_URL ?? defaultDatabaseUrl;
883
+ const url = new URL(databaseUrl);
884
+ if (url.protocol !== "mongodb:") {
885
+ throw new Error("DATABASE_URL must use the mongodb:// protocol.");
886
+ }
887
+
888
+ const port = Number(url.port || "27017");
889
+ if (!Number.isInteger(port) || port < 1 || port > 65535) {
890
+ throw new Error(\`DATABASE_URL has an invalid MongoDB port: \${url.port}\`);
891
+ }
892
+
893
+ return {
894
+ databaseUrl,
895
+ port,
896
+ replSetName: url.searchParams.get("replicaSet") || "rs0",
897
+ };
898
+ }
899
+
900
+ function readPid() {
901
+ if (!existsSync(pidFile)) return null;
902
+ const raw = readFileSync(pidFile, "utf8").trim();
903
+ const pid = Number(raw);
904
+ return Number.isFinite(pid) && pid > 0 ? pid : null;
905
+ }
906
+
907
+ function isAlive(pid) {
908
+ try {
909
+ process.kill(pid, 0);
910
+ return true;
911
+ } catch {
912
+ return false;
913
+ }
914
+ }
915
+
916
+ function getChildCommand() {
917
+ const scriptPath = path.resolve(process.argv[1] ?? "");
918
+ const versions = process.versions;
919
+ if (versions.deno) return { command: process.execPath, args: ["run", "-A", scriptPath, "_run"] };
920
+ return { command: process.execPath, args: [scriptPath, "_run"] };
921
+ }
922
+
923
+ async function runServer() {
924
+ mkdirSync(dbPath, { recursive: true });
925
+ const config = getMongoConfig();
926
+ const memoryServer = await import("mongodb-memory-server");
927
+ const { MongoMemoryReplSet } = memoryServer.default ?? memoryServer;
928
+ const replSet = await MongoMemoryReplSet.create({
929
+ replSet: { name: config.replSetName, count: 1 },
930
+ instanceOpts: [{ port: config.port, storageEngine: "wiredTiger", dbPath }],
931
+ });
932
+ console.log(\`MongoDB server ready for \${config.databaseUrl}\`);
933
+ console.log(\`Data directory: \${dbPath}\`);
934
+ const shutdown = async () => {
935
+ await replSet.stop();
936
+ process.exit(0);
937
+ };
938
+ process.on("SIGINT", shutdown);
939
+ process.on("SIGTERM", shutdown);
940
+ }
941
+
942
+ async function up() {
943
+ mkdirSync(dataRoot, { recursive: true });
944
+ const existing = readPid();
945
+ if (existing !== null && isAlive(existing)) {
946
+ console.log(\`MongoDB is already running (PID \${existing}). Use \\\`db:down\\\` to stop.\`);
947
+ return;
948
+ }
949
+ if (existing !== null) rmSync(pidFile, { force: true });
950
+
951
+ writeFileSync(logFile, "");
952
+ const logFd = openSync(logFile, "a");
953
+ const { command, args } = getChildCommand();
954
+ const child = spawn(
955
+ command,
956
+ args,
957
+ {
958
+ detached: true,
959
+ stdio: ["ignore", logFd, logFd],
960
+ env: process.env,
961
+ },
962
+ );
963
+ closeSync(logFd);
964
+ if (typeof child.pid !== "number") throw new Error("Failed to spawn MongoDB child process.");
965
+ writeFileSync(pidFile, String(child.pid));
966
+ child.unref();
967
+
968
+ const start = Date.now();
969
+ while (Date.now() - start < readyTimeoutMs) {
970
+ if (!isAlive(child.pid)) {
971
+ console.error("MongoDB failed to start:");
972
+ console.error(readFileSync(logFile, "utf8"));
973
+ rmSync(pidFile, { force: true });
974
+ process.exit(1);
975
+ }
976
+ const log = readFileSync(logFile, "utf8");
977
+ if (log.includes("MongoDB server ready")) {
978
+ for (const line of log.split("\\n")) {
979
+ if (line.trim().length > 0) console.log(line);
980
+ }
981
+ console.log(\`Detached (PID \${child.pid}). Logs: \${logFile}\`);
982
+ console.log("Stop with \`db:down\` or wipe with \`db:reset\`.");
983
+ return;
984
+ }
985
+ await new Promise((resolve) => setTimeout(resolve, 100));
986
+ }
987
+ console.error(\`Timed out waiting for MongoDB after \${readyTimeoutMs}ms.\`);
988
+ console.error(readFileSync(logFile, "utf8"));
989
+ try {
990
+ process.kill(child.pid, "SIGTERM");
991
+ } catch {
992
+ // ignore
993
+ }
994
+ rmSync(pidFile, { force: true });
995
+ process.exit(1);
996
+ }
997
+
998
+ async function down(wipe) {
999
+ const pid = readPid();
1000
+ if (pid !== null && isAlive(pid)) {
1001
+ process.kill(pid, "SIGTERM");
1002
+ const deadline = Date.now() + 10_000;
1003
+ while (Date.now() < deadline && isAlive(pid)) {
1004
+ await new Promise((resolve) => setTimeout(resolve, 100));
1005
+ }
1006
+ if (isAlive(pid)) {
1007
+ console.warn(\`MongoDB (PID \${pid}) did not exit within 10s; sending SIGKILL.\`);
1008
+ try {
1009
+ process.kill(pid, "SIGKILL");
1010
+ } catch {
1011
+ // ignore
1012
+ }
1013
+ }
1014
+ console.log(\`Stopped MongoDB (PID \${pid}).\`);
1015
+ } else if (pid !== null) {
1016
+ console.log("MongoDB was not running (stale PID file).");
1017
+ } else {
1018
+ console.log("MongoDB is not running.");
1019
+ }
1020
+ rmSync(pidFile, { force: true });
1021
+ if (wipe) {
1022
+ rmSync(dataRoot, { recursive: true, force: true });
1023
+ console.log(\`Removed \${dataRoot}.\`);
1024
+ }
1025
+ }
1026
+
1027
+ const cmd = process.argv[2] ?? "up";
1028
+ switch (cmd) {
1029
+ case "up":
1030
+ await up();
1031
+ break;
1032
+ case "down":
1033
+ await down(false);
1034
+ break;
1035
+ case "reset":
1036
+ await down(true);
1037
+ break;
1038
+ case "_run":
1039
+ await runServer();
1040
+ break;
1041
+ default:
1042
+ console.error(\`Unknown command: \${cmd}. Use: up | down | reset\`);
1043
+ process.exit(2);
1044
+ }
1045
+ `;
1046
+ function getMongoMemoryScripts(packageManager) {
1047
+ switch (packageManager) {
1048
+ case "bun": return {
1049
+ "db:up": "bun --env-file=.env scripts/mongo.mjs up",
1050
+ "db:down": "bun --env-file=.env scripts/mongo.mjs down",
1051
+ "db:reset": "bun --env-file=.env scripts/mongo.mjs reset"
1052
+ };
1053
+ case "deno": return {
1054
+ "db:up": "deno run -A --env-file=.env scripts/mongo.mjs up",
1055
+ "db:down": "deno run -A --env-file=.env scripts/mongo.mjs down",
1056
+ "db:reset": "deno run -A --env-file=.env scripts/mongo.mjs reset"
1057
+ };
1058
+ default: return {
1059
+ "db:up": "node --env-file=.env scripts/mongo.mjs up",
1060
+ "db:down": "node --env-file=.env scripts/mongo.mjs down",
1061
+ "db:reset": "node --env-file=.env scripts/mongo.mjs reset"
1062
+ };
1063
+ }
1064
+ }
651
1065
  const requiredPrismaFileGroups = [
652
- ["prisma/schema.prisma", "packages/db/prisma/schema.prisma"],
653
- ["prisma/seed.ts", "packages/db/prisma/seed.ts"],
654
- ["prisma.config.ts", "packages/db/prisma.config.ts"],
1066
+ ["prisma/contract.prisma", "prisma/contract.ts"],
1067
+ ["prisma-next.config.ts"],
655
1068
  [
656
1069
  "src/lib/prisma.ts",
657
1070
  "src/lib/prisma.server.ts",
658
1071
  "src/lib/server/prisma.ts",
659
- "server/utils/prisma.ts",
660
- "packages/db/src/client.ts"
1072
+ "server/utils/prisma.ts"
661
1073
  ]
662
1074
  ];
663
- async function resolvePrismaProjectDir(projectDir) {
664
- const monorepoDbDir = path.join(projectDir, "packages/db");
665
- if (await fs.pathExists(path.join(monorepoDbDir, "prisma/schema.prisma"))) return monorepoDbDir;
666
- return projectDir;
1075
+ function getContractPath(authoring) {
1076
+ return `prisma/contract${authoring === "typescript" ? ".ts" : ".prisma"}`;
667
1077
  }
668
1078
  async function promptForDatabaseProvider() {
669
1079
  const databaseProvider = await select({
670
1080
  message: "Select your database",
671
1081
  initialValue: DEFAULT_DATABASE_PROVIDER,
672
- options: [
673
- {
674
- value: "postgresql",
675
- label: "PostgreSQL",
676
- hint: "Default"
677
- },
678
- {
679
- value: "mysql",
680
- label: "MySQL"
681
- },
682
- {
683
- value: "sqlite",
684
- label: "SQLite"
685
- },
686
- {
687
- value: "sqlserver",
688
- label: "SQL Server"
689
- },
690
- {
691
- value: "cockroachdb",
692
- label: "CockroachDB"
693
- }
694
- ]
1082
+ options: [{
1083
+ value: "postgres",
1084
+ label: "PostgreSQL",
1085
+ hint: "Relational models with typed ORM, relations, indexes, raw SQL"
1086
+ }, {
1087
+ value: "mongo",
1088
+ label: "MongoDB",
1089
+ hint: "Document models with typed ORM, indexes, aggregations"
1090
+ }]
695
1091
  });
696
1092
  if (isCancel(databaseProvider)) {
697
1093
  cancel("Operation cancelled.");
@@ -699,10 +1095,48 @@ async function promptForDatabaseProvider() {
699
1095
  }
700
1096
  return DatabaseProviderSchema.parse(databaseProvider);
701
1097
  }
1098
+ async function promptForAuthoringStyle() {
1099
+ const authoring = await select({
1100
+ message: "Choose contract authoring style",
1101
+ initialValue: DEFAULT_AUTHORING,
1102
+ options: [{
1103
+ value: "psl",
1104
+ label: "PSL",
1105
+ hint: "Schema syntax emits contract.json + types"
1106
+ }, {
1107
+ value: "typescript",
1108
+ label: "TypeScript",
1109
+ hint: "Builder API emits the same contract artifacts"
1110
+ }]
1111
+ });
1112
+ if (isCancel(authoring)) {
1113
+ cancel("Operation cancelled.");
1114
+ return;
1115
+ }
1116
+ return AuthoringStyleSchema.parse(authoring);
1117
+ }
1118
+ async function promptForPrismaPostgres() {
1119
+ const shouldUsePrismaPostgres = await confirm({
1120
+ message: "Provision a Prisma Postgres database?",
1121
+ active: "Provision Prisma Postgres",
1122
+ inactive: "Use my own database",
1123
+ initialValue: DEFAULT_INTERACTIVE_PRISMA_POSTGRES
1124
+ });
1125
+ if (isCancel(shouldUsePrismaPostgres)) {
1126
+ cancel("Operation cancelled.");
1127
+ return;
1128
+ }
1129
+ return Boolean(shouldUsePrismaPostgres);
1130
+ }
702
1131
  function getPackageManagerHint(option, detected) {
703
- if (option === detected) return "Detected";
704
- if (option === "bun") return "Fast runtime + package manager";
705
- if (option === "deno") return "Runtime + package manager";
1132
+ const hint = {
1133
+ npm: "Node.js default",
1134
+ pnpm: "Fast, disk-efficient Node.js package manager",
1135
+ yarn: "Yarn package manager",
1136
+ bun: "Fast runtime + package manager",
1137
+ deno: "Deno runtime + task runner"
1138
+ }[option];
1139
+ return option === detected ? `Detected; ${hint}` : hint;
706
1140
  }
707
1141
  async function promptForPackageManager(detectedPackageManager) {
708
1142
  const packageManager = await select({
@@ -744,7 +1178,9 @@ async function promptForPackageManager(detectedPackageManager) {
744
1178
  }
745
1179
  async function promptForDependencyInstall(packageManager) {
746
1180
  const shouldInstall = await confirm({
747
- message: `Install dependencies now with ${getInstallCommand(packageManager)}?`,
1181
+ message: `Install dependencies now with ${getInstallCommand(packageManager)}? You can run it later.`,
1182
+ active: "Install now",
1183
+ inactive: "Skip for now",
748
1184
  initialValue: true
749
1185
  });
750
1186
  if (isCancel(shouldInstall)) {
@@ -753,17 +1189,6 @@ async function promptForDependencyInstall(packageManager) {
753
1189
  }
754
1190
  return Boolean(shouldInstall);
755
1191
  }
756
- async function promptForPrismaPostgres() {
757
- const shouldUsePrismaPostgres = await confirm({
758
- message: "Use Prisma Postgres and auto-generate DATABASE_URL with create-db?",
759
- initialValue: true
760
- });
761
- if (isCancel(shouldUsePrismaPostgres)) {
762
- cancel("Operation cancelled.");
763
- return;
764
- }
765
- return Boolean(shouldUsePrismaPostgres);
766
- }
767
1192
  function getCommandErrorMessage(error) {
768
1193
  if (error instanceof Error && "stderr" in error) {
769
1194
  const stderr = String(error.stderr ?? "").trim();
@@ -775,17 +1200,29 @@ async function collectPrismaSetupContext(input, options = {}) {
775
1200
  const projectDir = path.resolve(options.projectDir ?? process.cwd());
776
1201
  const useDefaults = input.yes === true;
777
1202
  const verbose = input.verbose === true;
778
- const shouldGenerate = input.generate ?? DEFAULT_GENERATE;
1203
+ const shouldEmit = input.emit ?? DEFAULT_EMIT;
1204
+ let prismaNextSpec;
1205
+ try {
1206
+ prismaNextSpec = parsePrismaNextVersionSpec(input.prismaNextVersion);
1207
+ } catch (error) {
1208
+ cancel(error instanceof Error ? error.message : String(error));
1209
+ return;
1210
+ }
779
1211
  const databaseProvider = input.provider ?? (useDefaults ? DEFAULT_DATABASE_PROVIDER : await promptForDatabaseProvider());
780
1212
  if (!databaseProvider) return;
781
- const schemaPreset = input.schemaPreset ?? options.defaultSchemaPreset ?? DEFAULT_SCHEMA_PRESET$1;
782
1213
  const databaseUrl = input.databaseUrl;
783
- let shouldUsePrismaPostgres = false;
784
- if (databaseProvider === "postgresql" && !databaseUrl) {
785
- const prismaPostgresChoice = input.prismaPostgres ?? (useDefaults ? DEFAULT_PRISMA_POSTGRES : await promptForPrismaPostgres());
786
- if (prismaPostgresChoice === void 0) return;
787
- shouldUsePrismaPostgres = prismaPostgresChoice;
1214
+ const shouldUsePrismaPostgres = input.prismaPostgres ?? (databaseProvider === "postgres" && !databaseUrl && !useDefaults ? await promptForPrismaPostgres() : DEFAULT_AUTOMATED_PRISMA_POSTGRES);
1215
+ if (shouldUsePrismaPostgres === void 0) return;
1216
+ if (shouldUsePrismaPostgres && databaseProvider !== "postgres") {
1217
+ cancel("--prisma-postgres is only supported with --provider postgres.");
1218
+ return;
788
1219
  }
1220
+ if (shouldUsePrismaPostgres && databaseUrl) {
1221
+ cancel("Use either --database-url or --prisma-postgres, not both.");
1222
+ return;
1223
+ }
1224
+ const authoring = input.authoring ?? (useDefaults ? DEFAULT_AUTHORING : await promptForAuthoringStyle());
1225
+ if (!authoring) return;
789
1226
  const detectedPackageManager = await detectPackageManager(projectDir);
790
1227
  const packageManager = input.packageManager ?? (useDefaults ? detectedPackageManager : await promptForPackageManager(detectedPackageManager));
791
1228
  if (!packageManager) return;
@@ -794,25 +1231,23 @@ async function collectPrismaSetupContext(input, options = {}) {
794
1231
  return {
795
1232
  projectDir,
796
1233
  verbose,
797
- shouldGenerate,
1234
+ shouldEmit,
798
1235
  databaseProvider,
799
- schemaPreset,
1236
+ authoring,
800
1237
  databaseUrl,
801
1238
  shouldUsePrismaPostgres,
802
1239
  packageManager,
803
- shouldInstall
1240
+ shouldInstall,
1241
+ prismaNextSpec
804
1242
  };
805
1243
  }
806
1244
  function getDefaultDatabaseUrl(provider) {
807
1245
  switch (provider) {
808
- case "postgresql": return "postgresql://johndoe:randompassword@localhost:5432/mydb?schema=public";
809
- case "cockroachdb": return "postgresql://johndoe:randompassword@localhost:26257/mydb?schema=public";
810
- case "mysql": return "mysql://johndoe:randompassword@localhost:3306/mydb";
811
- case "sqlite": return "file:./dev.db";
812
- case "sqlserver": return "sqlserver://localhost:1433;database=mydb;user=SA;password=randompassword;";
1246
+ case "postgres": return "postgresql://user:password@localhost:5432/mydb";
1247
+ case "mongo": return "mongodb://localhost:27017/mydb?replicaSet=rs0&directConnection=true";
813
1248
  default: {
814
1249
  const exhaustiveCheck = provider;
815
- throw new Error(`Unsupported provider: ${String(exhaustiveCheck)}`);
1250
+ throw new Error(`Unsupported Prisma Next target: ${String(exhaustiveCheck)}`);
816
1251
  }
817
1252
  }
818
1253
  }
@@ -882,6 +1317,48 @@ async function ensureGitignoreEntry(projectDir, entry) {
882
1317
  const separator = existingContent.endsWith("\n") ? "" : "\n";
883
1318
  await fs.appendFile(gitignorePath, `${separator}${entry}\n`, "utf8");
884
1319
  }
1320
+ async function ensurePackageScripts(projectDir, scripts) {
1321
+ const packageJsonPath = path.join(projectDir, "package.json");
1322
+ if (!await fs.pathExists(packageJsonPath)) return;
1323
+ const packageJson = await fs.readJson(packageJsonPath);
1324
+ if (!packageJson.scripts) packageJson.scripts = {};
1325
+ let didChange = false;
1326
+ for (const [scriptName, command] of Object.entries(scripts)) if (typeof packageJson.scripts[scriptName] !== "string" || packageJson.scripts[scriptName].trim().length === 0) {
1327
+ packageJson.scripts[scriptName] = command;
1328
+ didChange = true;
1329
+ }
1330
+ if (didChange) await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
1331
+ }
1332
+ async function ensureMongoMemoryServerScript(projectDir) {
1333
+ const scriptPath = path.join(projectDir, "scripts", "mongo.mjs");
1334
+ if (await fs.pathExists(scriptPath)) return;
1335
+ await fs.ensureDir(path.dirname(scriptPath));
1336
+ await fs.writeFile(scriptPath, MONGO_MEMORY_SERVER_SCRIPT, "utf8");
1337
+ }
1338
+ async function ensureMongoMemoryServerDevDependency(projectDir) {
1339
+ const packageJsonPath = path.join(projectDir, "package.json");
1340
+ if (!await fs.pathExists(packageJsonPath)) return;
1341
+ const packageJson = await fs.readJson(packageJsonPath);
1342
+ if (!packageJson.devDependencies) packageJson.devDependencies = {};
1343
+ const memoryServerVersion = getDependencyVersion("mongodb-memory-server");
1344
+ if (packageJson.devDependencies["mongodb-memory-server"] === memoryServerVersion) return;
1345
+ packageJson.devDependencies["mongodb-memory-server"] = memoryServerVersion;
1346
+ packageJson.devDependencies = Object.fromEntries(Object.entries(packageJson.devDependencies).sort(([a], [b]) => a.localeCompare(b)));
1347
+ await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
1348
+ }
1349
+ async function writeMongoLocalHelpersForContext(context, projectDir) {
1350
+ if (context.databaseProvider !== "mongo" || context.databaseUrl) return true;
1351
+ try {
1352
+ await ensureMongoMemoryServerScript(projectDir);
1353
+ await ensureMongoMemoryServerDevDependency(projectDir);
1354
+ await ensurePackageScripts(projectDir, getMongoMemoryScripts(context.packageManager));
1355
+ await ensureGitignoreEntry(projectDir, ".mongo-data");
1356
+ return true;
1357
+ } catch (error) {
1358
+ cancel(getCommandErrorMessage(error));
1359
+ return false;
1360
+ }
1361
+ }
885
1362
  async function ensureRequiredPrismaFiles(projectDir) {
886
1363
  const missingFiles = [];
887
1364
  for (const candidates of requiredPrismaFileGroups) {
@@ -895,41 +1372,37 @@ async function ensureRequiredPrismaFiles(projectDir) {
895
1372
  }
896
1373
  if (!foundCandidate) missingFiles.push(candidates.join(" or "));
897
1374
  }
898
- if (missingFiles.length > 0) throw new Error(`Template is missing required Prisma files: ${missingFiles.join(", ")}`);
1375
+ if (missingFiles.length > 0) throw new Error(`Template is missing required Prisma Next files: ${missingFiles.join(", ")}`);
899
1376
  }
900
1377
  async function finalizePrismaFiles(options) {
901
1378
  const projectDir = options.projectDir ?? process.cwd();
902
- const prismaProjectDir = await resolvePrismaProjectDir(projectDir);
903
1379
  await ensureRequiredPrismaFiles(projectDir);
904
- const generatedDir = await fs.pathExists(path.join(prismaProjectDir, "server/utils/prisma.ts")) ? "server/generated" : "src/generated";
905
- await ensureEnvVarInEnv(prismaProjectDir, "DATABASE_URL", options.databaseUrl ?? getDefaultDatabaseUrl(options.provider), {
1380
+ await ensureEnvVarInEnv(projectDir, "DATABASE_URL", options.databaseUrl ?? getDefaultDatabaseUrl(options.provider), {
906
1381
  mode: options.databaseUrl ? "upsert" : "keep-existing",
907
1382
  comment: "Added by create-prisma"
908
1383
  });
909
1384
  if (options.claimUrl) {
910
- await ensureEnvVarInEnv(prismaProjectDir, "CLAIM_URL", options.claimUrl, {
1385
+ await ensureEnvVarInEnv(projectDir, "CLAIM_URL", options.claimUrl, {
911
1386
  mode: "upsert",
912
1387
  comment: PRISMA_POSTGRES_TEMPORARY_NOTICE
913
1388
  });
914
- await ensureEnvComment(prismaProjectDir, PRISMA_POSTGRES_TEMPORARY_NOTICE);
1389
+ await ensureEnvComment(projectDir, PRISMA_POSTGRES_TEMPORARY_NOTICE);
915
1390
  }
916
- await ensureGitignoreEntry(prismaProjectDir, generatedDir);
1391
+ await ensureGitignoreEntry(projectDir, ".env");
917
1392
  }
918
1393
  async function provisionPrismaPostgresIfNeeded(context, projectDir) {
919
1394
  if (!context.shouldUsePrismaPostgres) return { databaseUrl: context.databaseUrl };
920
1395
  const createDbCommand = getCreateDbCommand(context.packageManager);
921
- const prismaPostgresSpinner = spinner();
922
- prismaPostgresSpinner.start(`Provisioning Prisma Postgres with ${createDbCommand}...`);
1396
+ if (context.verbose) log.step(`Running ${createDbCommand}`);
923
1397
  try {
924
1398
  const prismaPostgresResult = await provisionPrismaPostgres(context.packageManager, projectDir);
925
- prismaPostgresSpinner.stop("Prisma Postgres database provisioned.");
1399
+ if (context.verbose) log.success("Prisma Postgres database provisioned.");
926
1400
  return {
927
1401
  databaseUrl: prismaPostgresResult.databaseUrl,
928
1402
  claimUrl: prismaPostgresResult.claimUrl
929
1403
  };
930
1404
  } catch (error) {
931
1405
  const errorMessage = error instanceof Error ? error.message : String(error);
932
- prismaPostgresSpinner.stop("Could not provision Prisma Postgres.");
933
1406
  return {
934
1407
  databaseUrl: context.databaseUrl,
935
1408
  warning: `Prisma Postgres provisioning failed: ${errorMessage}`
@@ -937,44 +1410,86 @@ async function provisionPrismaPostgresIfNeeded(context, projectDir) {
937
1410
  }
938
1411
  }
939
1412
  async function writeDependenciesForContext(context, projectDir) {
940
- const prismaProjectDir = await resolvePrismaProjectDir(projectDir);
941
1413
  try {
942
- await writePrismaDependencies(context.databaseProvider, context.packageManager, prismaProjectDir);
1414
+ await writePrismaDependencies(context.databaseProvider, context.packageManager, context.authoring, projectDir, context.prismaNextSpec);
943
1415
  return true;
944
1416
  } catch (error) {
945
1417
  cancel(getCommandErrorMessage(error));
946
1418
  return false;
947
1419
  }
948
1420
  }
1421
+ function getPrismaNextCliPackageSpecifier(prismaNextSpec = DEFAULT_PRISMA_NEXT_SPEC) {
1422
+ return getPrismaNextPackageSpecifier("prisma-next", prismaNextSpec);
1423
+ }
1424
+ function getPrismaNextInitTarget(provider) {
1425
+ return provider === "mongo" ? "mongodb" : "postgres";
1426
+ }
1427
+ function getPrismaNextInitCliArgs(packageManager, prismaNextArgs, prismaNextSpec = DEFAULT_PRISMA_NEXT_SPEC) {
1428
+ if (packageManager === "npm") return {
1429
+ command: "npx",
1430
+ args: [
1431
+ "--yes",
1432
+ getPrismaNextCliPackageSpecifier(prismaNextSpec),
1433
+ "init",
1434
+ ...prismaNextArgs
1435
+ ]
1436
+ };
1437
+ return getPackageExecutionArgs(packageManager, [
1438
+ getPrismaNextCliPackageSpecifier(prismaNextSpec),
1439
+ "init",
1440
+ ...prismaNextArgs
1441
+ ]);
1442
+ }
1443
+ function getPrismaNextInitCliCommand(packageManager, prismaNextArgs, prismaNextSpec = DEFAULT_PRISMA_NEXT_SPEC) {
1444
+ const execution = getPrismaNextInitCliArgs(packageManager, prismaNextArgs, prismaNextSpec);
1445
+ return [execution.command, ...execution.args].join(" ");
1446
+ }
1447
+ async function runPrismaNextInitForContext(context, projectDir) {
1448
+ const initArgs = [
1449
+ "--yes",
1450
+ "--force",
1451
+ "--target",
1452
+ getPrismaNextInitTarget(context.databaseProvider),
1453
+ "--authoring",
1454
+ context.authoring,
1455
+ "--schema-path",
1456
+ getContractPath(context.authoring),
1457
+ "--no-install"
1458
+ ];
1459
+ const initCommand = getPrismaNextInitCliCommand(context.packageManager, initArgs, context.prismaNextSpec);
1460
+ if (context.verbose) log.step(`Running ${initCommand}`);
1461
+ try {
1462
+ const initExecution = getPrismaNextInitCliArgs(context.packageManager, initArgs, context.prismaNextSpec);
1463
+ await execa(initExecution.command, initExecution.args, {
1464
+ cwd: projectDir,
1465
+ stdio: context.verbose ? "inherit" : "pipe",
1466
+ env: {
1467
+ ...process.env,
1468
+ CI: "1"
1469
+ }
1470
+ });
1471
+ if (context.verbose) log.success("Prisma Next project files ready.");
1472
+ return true;
1473
+ } catch (error) {
1474
+ if (context.verbose) log.warn("Could not run Prisma Next init.");
1475
+ cancel(`Failed to run ${initCommand}: ${getCommandErrorMessage(error)}`);
1476
+ return false;
1477
+ }
1478
+ }
949
1479
  async function installDependenciesForContext(context, projectDir) {
950
1480
  if (!context.shouldInstall) return true;
951
1481
  const installCommand = getInstallCommand(context.packageManager);
952
- if (context.verbose) {
953
- log.step(`Running ${installCommand}`);
954
- try {
955
- await installProjectDependencies(context.packageManager, projectDir, { verbose: context.verbose });
956
- log.success("Dependencies installed.");
957
- return true;
958
- } catch (error) {
959
- cancel(`Failed to run ${installCommand}: ${getCommandErrorMessage(error)}`);
960
- return false;
961
- }
962
- }
963
- const installSpinner = spinner();
964
- installSpinner.start(`Running ${installCommand}...`);
1482
+ if (context.verbose) log.step(`Running ${installCommand}`);
965
1483
  try {
966
1484
  await installProjectDependencies(context.packageManager, projectDir, { verbose: context.verbose });
967
- installSpinner.stop("Dependencies installed.");
1485
+ if (context.verbose) log.success("Dependencies installed.");
968
1486
  return true;
969
1487
  } catch (error) {
970
- installSpinner.stop("Could not install dependencies.");
971
1488
  cancel(`Failed to run ${installCommand}: ${getCommandErrorMessage(error)}`);
972
1489
  return false;
973
1490
  }
974
1491
  }
975
1492
  async function finalizePrismaFilesForContext(context, projectDir, provisionResult) {
976
- const initSpinner = spinner();
977
- initSpinner.start("Preparing Prisma files...");
978
1493
  try {
979
1494
  await finalizePrismaFiles({
980
1495
  provider: context.databaseProvider,
@@ -982,746 +1497,165 @@ async function finalizePrismaFilesForContext(context, projectDir, provisionResul
982
1497
  claimUrl: provisionResult.claimUrl,
983
1498
  projectDir
984
1499
  });
985
- initSpinner.stop("Prisma files ready.");
1500
+ if (context.verbose) log.success("Prisma Next environment configured.");
986
1501
  return true;
987
1502
  } catch (error) {
988
- initSpinner.stop("Could not prepare Prisma files.");
989
1503
  cancel(getCommandErrorMessage(error));
990
1504
  return false;
991
1505
  }
992
1506
  }
993
- async function generatePrismaClientForContext(context, projectDir) {
994
- const prismaProjectDir = await resolvePrismaProjectDir(projectDir);
995
- if (!context.shouldGenerate) return { didGenerateClient: false };
996
- const generateCommand = getPrismaCliCommand(context.packageManager, ["generate"]);
997
- if (context.verbose) log.step(`Running ${generateCommand}`);
998
- const generateSpinner = context.verbose ? void 0 : spinner();
999
- generateSpinner?.start("Generating Prisma Client...");
1507
+ function getPrismaNextCliCommand(packageManager, prismaNextArgs) {
1508
+ if (packageManager === "deno") return `deno run -A --env-file=.env ${getDenoPrismaSpecifier()} ${prismaNextArgs.join(" ")}`;
1509
+ return getLocalPackageBinaryCommand(packageManager, "prisma-next", prismaNextArgs);
1510
+ }
1511
+ function getPrismaNextCliArgs(packageManager, prismaNextArgs) {
1512
+ if (packageManager === "deno") return {
1513
+ command: "deno",
1514
+ args: [
1515
+ "run",
1516
+ "-A",
1517
+ "--env-file=.env",
1518
+ getDenoPrismaSpecifier(),
1519
+ ...prismaNextArgs
1520
+ ]
1521
+ };
1522
+ return getLocalPackageBinaryArgs(packageManager, "prisma-next", prismaNextArgs);
1523
+ }
1524
+ async function emitPrismaNextContractForContext(context, projectDir) {
1525
+ if (!context.shouldEmit) return { didEmitContract: false };
1526
+ if (!context.shouldInstall) return {
1527
+ didEmitContract: false,
1528
+ warning: "Skipped contract emit because dependencies were not installed."
1529
+ };
1530
+ const emitCommand = getPrismaNextCliCommand(context.packageManager, ["contract", "emit"]);
1531
+ if (context.verbose) log.step(`Running ${emitCommand}`);
1000
1532
  try {
1001
- const generateArgs = getPrismaCliArgs(context.packageManager, ["generate"]);
1002
- await execa(generateArgs.command, generateArgs.args, {
1003
- cwd: prismaProjectDir,
1533
+ const emitArgs = getPrismaNextCliArgs(context.packageManager, ["contract", "emit"]);
1534
+ await execa(emitArgs.command, emitArgs.args, {
1535
+ cwd: projectDir,
1004
1536
  stdio: context.verbose ? "inherit" : "pipe"
1005
1537
  });
1006
- if (context.verbose) log.success("Prisma Client generated.");
1007
- else generateSpinner?.stop("Prisma Client generated.");
1008
- return { didGenerateClient: true };
1538
+ if (context.verbose) log.success("Prisma Next contract artifacts emitted.");
1539
+ return { didEmitContract: true };
1009
1540
  } catch (error) {
1010
- if (context.verbose) log.warn("Could not generate Prisma Client.");
1011
- else generateSpinner?.stop("Could not generate Prisma Client.");
1541
+ if (context.verbose) log.warn("Could not emit Prisma Next contract.");
1012
1542
  return {
1013
- didGenerateClient: false,
1014
- warning: `Prisma generate failed: ${getCommandErrorMessage(error)}`
1543
+ didEmitContract: false,
1544
+ warning: `Contract emit failed: ${getCommandErrorMessage(error)}`
1015
1545
  };
1016
1546
  }
1017
1547
  }
1018
- function buildWarningLines(provisionWarning, generateWarning) {
1548
+ function buildWarningLines(provisionWarning, emitWarning) {
1019
1549
  const warningLines = [];
1020
1550
  if (provisionWarning) warningLines.push(`- ${provisionWarning}`);
1021
- if (generateWarning) warningLines.push(`- ${generateWarning}`);
1551
+ if (emitWarning) warningLines.push(`- ${emitWarning}`);
1022
1552
  return warningLines;
1023
1553
  }
1024
1554
  function buildNextStepsForContext(opts) {
1025
- const { context, options, didGenerateClient } = opts;
1555
+ const { context, options, didEmitContract } = opts;
1026
1556
  const nextSteps = [...options.prependNextSteps ?? []];
1027
- if (!context.shouldInstall) nextSteps.push(`- ${getInstallCommand(context.packageManager)}`);
1028
- if (!didGenerateClient || !context.shouldGenerate) nextSteps.push(`- ${getRunScriptCommand(context.packageManager, "db:generate")}`);
1029
- nextSteps.push(`- ${getRunScriptCommand(context.packageManager, "db:migrate")}`);
1030
- nextSteps.push(`- ${getRunScriptCommand(context.packageManager, "db:seed")}`);
1031
- if (options.includeDevNextStep) nextSteps.push(`- ${getRunScriptCommand(context.packageManager, "dev")}`);
1032
- return nextSteps;
1033
- }
1034
- async function executePrismaSetupContext(context, options = {}) {
1035
- const projectDir = path.resolve(options.projectDir ?? context.projectDir);
1036
- const provisionResult = await provisionPrismaPostgresIfNeeded(context, projectDir);
1037
- if (!provisionResult) return false;
1038
- if (!await writeDependenciesForContext(context, projectDir)) return false;
1039
- if (!await installDependenciesForContext(context, projectDir)) return false;
1040
- if (!await finalizePrismaFilesForContext(context, projectDir, provisionResult)) return false;
1041
- const generateResult = await generatePrismaClientForContext(context, projectDir);
1042
- const warningLines = buildWarningLines(provisionResult.warning, generateResult.warning);
1043
- const nextSteps = buildNextStepsForContext({
1044
- context,
1045
- options,
1046
- didGenerateClient: generateResult.didGenerateClient
1557
+ if (!context.shouldInstall) nextSteps.push({
1558
+ command: getInstallCommand(context.packageManager),
1559
+ description: "Install the project dependencies."
1047
1560
  });
1048
- outro(`Setup complete.${warningLines.length > 0 ? `\n\n${warningLines.join("\n")}` : ""}
1049
-
1050
- Next steps:
1051
- ${nextSteps.join("\n")}`);
1052
- return true;
1053
- }
1054
-
1055
- //#endregion
1056
- //#region src/tasks/setup-addons.ts
1057
- const DEFAULT_ADDON_SCOPE = "project";
1058
- const DEFAULT_SKILLS_AGENTS = [
1059
- "claude-code",
1060
- "codex",
1061
- "cursor"
1062
- ];
1063
- const DEFAULT_MCP_AGENTS = [
1064
- "claude-code",
1065
- "codex",
1066
- "cursor"
1067
- ];
1068
- const DEFAULT_EXTENSION_TARGETS = ["vscode", "cursor"];
1069
- const PRISMA_MCP_SERVER = "https://mcp.prisma.io/mcp";
1070
- const ADDON_OPTIONS = [
1071
- {
1072
- value: "skills",
1073
- label: "Skills",
1074
- hint: "Install curated Prisma skills to your selected coding agents"
1075
- },
1076
- {
1077
- value: "mcp",
1078
- label: "MCP",
1079
- hint: "Configure Prisma MCP server in agent MCP config files"
1080
- },
1081
- {
1082
- value: "extension",
1083
- label: "IDE Extension",
1084
- hint: "Install Prisma extension in selected IDEs (VS Code, Cursor, Windsurf)"
1085
- }
1086
- ];
1087
- const SKILLS_AGENT_OPTIONS = [
1088
- {
1089
- value: "cursor",
1090
- label: "Cursor"
1091
- },
1092
- {
1093
- value: "claude-code",
1094
- label: "Claude Code"
1095
- },
1096
- {
1097
- value: "cline",
1098
- label: "Cline"
1099
- },
1100
- {
1101
- value: "github-copilot",
1102
- label: "GitHub Copilot"
1103
- },
1104
- {
1105
- value: "codex",
1106
- label: "Codex"
1107
- },
1108
- {
1109
- value: "opencode",
1110
- label: "OpenCode"
1111
- },
1112
- {
1113
- value: "windsurf",
1114
- label: "Windsurf"
1115
- },
1116
- {
1117
- value: "goose",
1118
- label: "Goose"
1119
- },
1120
- {
1121
- value: "roo",
1122
- label: "Roo Code"
1123
- },
1124
- {
1125
- value: "kilo",
1126
- label: "Kilo Code"
1127
- },
1128
- {
1129
- value: "gemini-cli",
1130
- label: "Gemini CLI"
1131
- },
1132
- {
1133
- value: "antigravity",
1134
- label: "Antigravity"
1135
- },
1136
- {
1137
- value: "openhands",
1138
- label: "OpenHands"
1139
- },
1140
- {
1141
- value: "trae",
1142
- label: "Trae"
1143
- },
1144
- {
1145
- value: "amp",
1146
- label: "Amp"
1147
- },
1148
- {
1149
- value: "pi",
1150
- label: "Pi"
1151
- },
1152
- {
1153
- value: "qoder",
1154
- label: "Qoder"
1155
- },
1156
- {
1157
- value: "qwen-code",
1158
- label: "Qwen Code"
1159
- },
1160
- {
1161
- value: "kiro-cli",
1162
- label: "Kiro CLI"
1163
- },
1164
- {
1165
- value: "droid",
1166
- label: "Droid"
1167
- },
1168
- {
1169
- value: "command-code",
1170
- label: "Command Code"
1171
- },
1172
- {
1173
- value: "clawdbot",
1174
- label: "Clawdbot"
1175
- },
1176
- {
1177
- value: "zencoder",
1178
- label: "Zencoder"
1179
- },
1180
- {
1181
- value: "neovate",
1182
- label: "Neovate"
1183
- },
1184
- {
1185
- value: "mcpjam",
1186
- label: "MCPJam"
1187
- }
1188
- ];
1189
- const MCP_AGENT_OPTIONS = [
1190
- {
1191
- value: "claude-code",
1192
- label: "Claude Code"
1193
- },
1194
- {
1195
- value: "codex",
1196
- label: "Codex"
1197
- },
1198
- {
1199
- value: "cursor",
1200
- label: "Cursor"
1201
- },
1202
- {
1203
- value: "vscode",
1204
- label: "VS Code"
1205
- },
1206
- {
1207
- value: "github-copilot-cli",
1208
- label: "GitHub Copilot CLI"
1209
- },
1210
- {
1211
- value: "opencode",
1212
- label: "OpenCode"
1213
- },
1214
- {
1215
- value: "gemini-cli",
1216
- label: "Gemini CLI"
1217
- },
1218
- {
1219
- value: "goose",
1220
- label: "Goose"
1221
- },
1222
- {
1223
- value: "zed",
1224
- label: "Zed"
1225
- },
1226
- {
1227
- value: "antigravity",
1228
- label: "Antigravity"
1229
- },
1230
- {
1231
- value: "cline",
1232
- label: "Cline VS Code Extension"
1233
- },
1234
- {
1235
- value: "cline-cli",
1236
- label: "Cline CLI"
1237
- },
1238
- {
1239
- value: "claude-desktop",
1240
- label: "Claude Desktop"
1241
- },
1242
- {
1243
- value: "mcporter",
1244
- label: "MCPorter"
1245
- }
1246
- ];
1247
- const SHARED_PRISMA_SKILLS = [
1248
- "prisma-cli",
1249
- "prisma-client-api",
1250
- "prisma-database-setup",
1251
- "prisma-upgrade-v7"
1252
- ];
1253
- function getAvailablePrismaSkills(provider) {
1254
- if (provider === "postgresql") return [...SHARED_PRISMA_SKILLS, "prisma-postgres"];
1255
- return [...SHARED_PRISMA_SKILLS];
1256
- }
1257
- function getSkillOptions(provider) {
1258
- const available = getAvailablePrismaSkills(provider);
1259
- const options = {
1260
- "prisma-cli": {
1261
- value: "prisma-cli",
1262
- label: "prisma-cli",
1263
- hint: "Prisma CLI reference"
1264
- },
1265
- "prisma-client-api": {
1266
- value: "prisma-client-api",
1267
- label: "prisma-client-api",
1268
- hint: "Prisma Client query patterns"
1269
- },
1270
- "prisma-database-setup": {
1271
- value: "prisma-database-setup",
1272
- label: "prisma-database-setup",
1273
- hint: "Database provider setup guides"
1274
- },
1275
- "prisma-upgrade-v7": {
1276
- value: "prisma-upgrade-v7",
1277
- label: "prisma-upgrade-v7",
1278
- hint: "v6 to v7 migration guide"
1279
- },
1280
- "prisma-postgres": {
1281
- value: "prisma-postgres",
1282
- label: "prisma-postgres",
1283
- hint: "Prisma Postgres workflows"
1284
- }
1285
- };
1286
- return available.map((skill) => options[skill]);
1287
- }
1288
- function collectAddonsFromInput(input) {
1289
- const addons = [];
1290
- if (input.skills === true) addons.push("skills");
1291
- if (input.mcp === true) addons.push("mcp");
1292
- if (input.extension === true) addons.push("extension");
1293
- return uniqueValues(addons);
1294
- }
1295
- const EXTENSION_TARGET_OPTIONS = [
1296
- {
1297
- value: "vscode",
1298
- label: "VS Code",
1299
- hint: "Uses the `code` CLI"
1300
- },
1301
- {
1302
- value: "cursor",
1303
- label: "Cursor",
1304
- hint: "Uses the `cursor` CLI"
1305
- },
1306
- {
1307
- value: "windsurf",
1308
- label: "Windsurf",
1309
- hint: "Uses the `windsurf` CLI"
1310
- }
1311
- ];
1312
- function uniqueValues(values) {
1313
- return Array.from(new Set(values));
1314
- }
1315
- function getRecommendedPrismaSkills(provider, shouldUsePrismaPostgres) {
1316
- const skills = [...getAvailablePrismaSkills(provider)];
1317
- if (!shouldUsePrismaPostgres) return skills.filter((skill) => skill !== "prisma-postgres");
1318
- return uniqueValues(skills);
1319
- }
1320
- async function promptForAddons() {
1321
- const selectedAddons = await multiselect({
1322
- message: "Select add-ons (optional)",
1323
- options: ADDON_OPTIONS,
1324
- required: false
1561
+ if (!didEmitContract || !context.shouldEmit) nextSteps.push({
1562
+ command: getRunScriptCommand(context.packageManager, "contract:emit"),
1563
+ description: "Emit contract.json and TypeScript types from your Prisma Next contract."
1325
1564
  });
1326
- if (isCancel(selectedAddons)) {
1327
- cancel("Operation cancelled.");
1328
- return;
1329
- }
1330
- return uniqueValues(selectedAddons);
1331
- }
1332
- async function promptForAddonScope() {
1333
- const selectedScope = await select({
1334
- message: "Where should add-ons write config?",
1335
- initialValue: DEFAULT_ADDON_SCOPE,
1336
- options: [{
1337
- value: "project",
1338
- label: "Project",
1339
- hint: "Recommended for teams (checked into the project when applicable)"
1340
- }, {
1341
- value: "global",
1342
- label: "Global",
1343
- hint: "Personal machine-level setup"
1344
- }]
1565
+ if (context.databaseProvider === "postgres") nextSteps.push({
1566
+ command: getRunScriptCommand(context.packageManager, "db:init"),
1567
+ description: "Create the initial PostgreSQL database objects and sign the database."
1345
1568
  });
1346
- if (isCancel(selectedScope)) {
1347
- cancel("Operation cancelled.");
1348
- return;
1349
- }
1350
- return selectedScope;
1351
- }
1352
- async function promptForPrismaSkills(provider, recommendedSkills) {
1353
- const options = getSkillOptions(provider);
1354
- const optionValues = new Set(options.map((option) => option.value));
1355
- const selectedSkills = await multiselect({
1356
- message: "Select Prisma skills",
1357
- options,
1358
- required: false,
1359
- initialValues: recommendedSkills.filter((skill) => optionValues.has(skill))
1569
+ if (context.databaseProvider === "mongo" && !context.databaseUrl) nextSteps.push({
1570
+ command: getRunScriptCommand(context.packageManager, "db:up"),
1571
+ description: "Start the local MongoDB replica set with mongodb-memory-server. Stop with `db:down`, wipe with `db:reset`."
1360
1572
  });
1361
- if (isCancel(selectedSkills)) {
1362
- cancel("Operation cancelled.");
1363
- return;
1364
- }
1365
- return uniqueValues(selectedSkills);
1366
- }
1367
- async function promptForSkillsAgents() {
1368
- const selectedAgents = await multiselect({
1369
- message: "Select agents for skills",
1370
- options: SKILLS_AGENT_OPTIONS,
1371
- required: false,
1372
- initialValues: [...DEFAULT_SKILLS_AGENTS]
1573
+ nextSteps.push({
1574
+ command: getRunScriptCommand(context.packageManager, "migration:plan"),
1575
+ description: "Compare the contract to the database and write a migration plan."
1373
1576
  });
1374
- if (isCancel(selectedAgents)) {
1375
- cancel("Operation cancelled.");
1376
- return;
1377
- }
1378
- return uniqueValues(selectedAgents);
1379
- }
1380
- async function promptForMcpAgents() {
1381
- const selectedAgents = await multiselect({
1382
- message: "Select agents for MCP",
1383
- options: MCP_AGENT_OPTIONS,
1384
- required: false,
1385
- initialValues: [...DEFAULT_MCP_AGENTS]
1577
+ nextSteps.push({
1578
+ command: getRunScriptCommand(context.packageManager, "migrate"),
1579
+ description: "Apply the planned migration to the database."
1386
1580
  });
1387
- if (isCancel(selectedAgents)) {
1388
- cancel("Operation cancelled.");
1389
- return;
1390
- }
1391
- return uniqueValues(selectedAgents);
1392
- }
1393
- async function promptForExtensionTargets() {
1394
- const selectedTargets = await multiselect({
1395
- message: "Select IDEs for extension install",
1396
- options: EXTENSION_TARGET_OPTIONS,
1397
- required: false,
1398
- initialValues: DEFAULT_EXTENSION_TARGETS
1581
+ nextSteps.push({
1582
+ command: getRunScriptCommand(context.packageManager, "db:seed"),
1583
+ description: "Insert the sample users from prisma/seed.ts."
1399
1584
  });
1400
- if (isCancel(selectedTargets)) {
1401
- cancel("Operation cancelled.");
1402
- return;
1403
- }
1404
- return uniqueValues(selectedTargets);
1405
- }
1406
- async function collectCreateAddonSetupContext(input, options) {
1407
- const hasExplicitAddonSelection = input.skills !== void 0 || input.mcp !== void 0 || input.extension !== void 0;
1408
- const selectedFromInput = collectAddonsFromInput(input);
1409
- const selectedAddons = selectedFromInput.length > 0 ? selectedFromInput : hasExplicitAddonSelection ? [] : options.useDefaults ? [] : await promptForAddons();
1410
- if (!selectedAddons) return;
1411
- const addons = uniqueValues(selectedAddons);
1412
- if (addons.length === 0) return null;
1413
- const scope = addons.includes("skills") || addons.includes("mcp") ? options.useDefaults ? DEFAULT_ADDON_SCOPE : await promptForAddonScope() : DEFAULT_ADDON_SCOPE;
1414
- if (!scope) return;
1415
- const recommendedSkills = getRecommendedPrismaSkills(options.provider, options.shouldUsePrismaPostgres);
1416
- const skills = !addons.includes("skills") ? [] : options.useDefaults ? recommendedSkills : await promptForPrismaSkills(options.provider, recommendedSkills);
1417
- if (!skills) return;
1418
- const skillsAgents = !addons.includes("skills") ? [] : options.useDefaults ? [...DEFAULT_SKILLS_AGENTS] : await promptForSkillsAgents();
1419
- if (!skillsAgents) return;
1420
- const mcpAgents = !addons.includes("mcp") ? [] : options.useDefaults ? [...DEFAULT_MCP_AGENTS] : await promptForMcpAgents();
1421
- if (!mcpAgents) return;
1422
- const extensionTargets = !addons.includes("extension") ? [] : options.useDefaults ? [...DEFAULT_EXTENSION_TARGETS] : await promptForExtensionTargets();
1423
- if (!extensionTargets) return;
1424
- return {
1425
- addons,
1426
- scope,
1427
- skills,
1428
- skillsAgents: uniqueValues(skillsAgents),
1429
- mcpAgents: uniqueValues(mcpAgents),
1430
- extensionTargets: uniqueValues(extensionTargets)
1431
- };
1432
- }
1433
- async function executeExternalCommand(params) {
1434
- await execa(params.command, params.args, {
1435
- cwd: params.cwd,
1436
- stdio: params.verbose ? "inherit" : "pipe",
1437
- env: {
1438
- ...process.env,
1439
- CI: "true"
1440
- }
1585
+ if (options.includeDevNextStep) nextSteps.push({
1586
+ command: getRunScriptCommand(context.packageManager, "dev"),
1587
+ description: "Start the development server."
1441
1588
  });
1589
+ return nextSteps;
1442
1590
  }
1443
- async function installSkillsAddon(params) {
1444
- if (params.agents.length === 0 || params.skills.length === 0) return "Skipped skills addon because no skills or agents were selected.";
1445
- const scopeArgs = params.scope === "global" ? ["-g"] : [];
1446
- const skillArgs = params.skills.flatMap((skill) => ["-s", skill]);
1447
- const agentArgs = params.agents.flatMap((agent) => ["-a", agent]);
1448
- const commandArgs = [
1449
- "skills@latest",
1450
- "add",
1451
- "prisma/skills",
1452
- ...scopeArgs,
1453
- ...skillArgs,
1454
- ...agentArgs,
1455
- "-y"
1456
- ];
1457
- const execution = getPackageExecutionArgs(params.packageManager, commandArgs);
1458
- try {
1459
- await executeExternalCommand({
1460
- command: execution.command,
1461
- args: execution.args,
1462
- cwd: params.projectDir,
1463
- verbose: params.verbose
1464
- });
1465
- return;
1466
- } catch (error) {
1467
- return `Skills addon failed: ${error instanceof Error ? error.message : String(error)}`;
1468
- }
1469
- }
1470
- async function installMcpAddon(params) {
1471
- if (params.agents.length === 0) return "Skipped MCP addon because no agents were selected.";
1472
- const scopeArgs = params.scope === "global" ? ["-g"] : [];
1473
- const agentArgs = params.agents.flatMap((agent) => ["-a", agent]);
1474
- const commandArgs = [
1475
- "add-mcp@latest",
1476
- PRISMA_MCP_SERVER,
1477
- ...scopeArgs,
1478
- ...agentArgs,
1479
- "--name",
1480
- "prisma",
1481
- "--gitignore",
1482
- "-y"
1483
- ];
1484
- const execution = getPackageExecutionArgs(params.packageManager, commandArgs);
1485
- try {
1486
- await executeExternalCommand({
1487
- command: execution.command,
1488
- args: execution.args,
1489
- cwd: params.projectDir,
1490
- verbose: params.verbose
1491
- });
1492
- return;
1493
- } catch (error) {
1494
- return `MCP addon failed: ${error instanceof Error ? error.message : String(error)}`;
1495
- }
1591
+ function formatNextSteps(nextSteps) {
1592
+ return nextSteps.map((step) => `${step.command}\n ${step.description}`).join("\n\n");
1496
1593
  }
1497
- function getExtensionInstallBinary(target) {
1498
- switch (target) {
1499
- case "vscode": return "code";
1500
- case "cursor": return "cursor";
1501
- case "windsurf": return "windsurf";
1502
- default: {
1503
- const exhaustiveCheck = target;
1504
- throw new Error(`Unsupported extension target: ${String(exhaustiveCheck)}`);
1505
- }
1506
- }
1594
+ function formatAgentPrompt() {
1595
+ return [
1596
+ "Ask your agent:",
1597
+ "What can I do with Prisma Next?",
1598
+ "",
1599
+ "Learn more:",
1600
+ `Docs: prisma-next.md`,
1601
+ "Skills: https://github.com/prisma/prisma-next/tree/main/skills"
1602
+ ].join("\n");
1507
1603
  }
1508
- async function installExtensionAddon(params) {
1509
- if (params.targets.length === 0) return ["Skipped extension addon because no IDE targets were selected."];
1510
- const warnings = [];
1511
- for (const target of params.targets) {
1512
- const binary = getExtensionInstallBinary(target);
1513
- try {
1514
- await executeExternalCommand({
1515
- command: binary,
1516
- args: ["--version"],
1517
- cwd: params.projectDir,
1518
- verbose: false
1519
- });
1520
- } catch {
1521
- warnings.push(`Skipped ${target} extension install because the \`${binary}\` CLI is not available.`);
1522
- continue;
1523
- }
1524
- try {
1525
- await executeExternalCommand({
1526
- command: binary,
1527
- args: [
1528
- "--install-extension",
1529
- "Prisma.prisma",
1530
- "--force"
1531
- ],
1532
- cwd: params.projectDir,
1533
- verbose: params.verbose
1534
- });
1535
- } catch (error) {
1536
- warnings.push(`${target} extension install failed: ${error instanceof Error ? error.message : String(error)}`);
1537
- }
1604
+ async function executePrismaSetupContext(context, options = {}) {
1605
+ const projectDir = path.resolve(options.projectDir ?? context.projectDir);
1606
+ const progressSpinner = context.verbose ? void 0 : options.progressSpinner ?? spinner();
1607
+ if (progressSpinner !== void 0 && !options.progressSpinner) progressSpinner.start("Creating Prisma Next project...");
1608
+ const stopProgressOnFailure = () => {
1609
+ progressSpinner?.stop("Could not create Prisma Next project.");
1610
+ };
1611
+ if (context.shouldUsePrismaPostgres) progressSpinner?.message("Provisioning Prisma Postgres...");
1612
+ const provisionResult = await provisionPrismaPostgresIfNeeded(context, projectDir);
1613
+ if (!provisionResult) {
1614
+ stopProgressOnFailure();
1615
+ return false;
1538
1616
  }
1539
- return warnings;
1540
- }
1541
- async function executeCreateAddonSetupContext(params) {
1542
- const { context, packageManager, projectDir, verbose } = params;
1543
- const addonSpinner = spinner();
1544
- addonSpinner.start("Applying selected add-ons...");
1545
- const warnings = [];
1546
- if (context.addons.includes("skills")) {
1547
- const warning = await installSkillsAddon({
1548
- packageManager,
1549
- projectDir,
1550
- scope: context.scope,
1551
- skills: context.skills,
1552
- agents: context.skillsAgents,
1553
- verbose
1554
- });
1555
- if (warning) warnings.push(warning);
1617
+ progressSpinner?.message("Preparing Prisma Next project files...");
1618
+ if (!await runPrismaNextInitForContext(context, projectDir)) {
1619
+ stopProgressOnFailure();
1620
+ return false;
1556
1621
  }
1557
- if (context.addons.includes("mcp")) {
1558
- const warning = await installMcpAddon({
1559
- packageManager,
1560
- projectDir,
1561
- scope: context.scope,
1562
- agents: context.mcpAgents,
1563
- verbose
1564
- });
1565
- if (warning) warnings.push(warning);
1622
+ if (!await writeDependenciesForContext(context, projectDir)) {
1623
+ stopProgressOnFailure();
1624
+ return false;
1566
1625
  }
1567
- if (context.addons.includes("extension")) {
1568
- const extensionWarnings = await installExtensionAddon({
1569
- projectDir,
1570
- verbose,
1571
- targets: context.extensionTargets
1572
- });
1573
- warnings.push(...extensionWarnings);
1626
+ if (!await writeMongoLocalHelpersForContext(context, projectDir)) {
1627
+ stopProgressOnFailure();
1628
+ return false;
1574
1629
  }
1575
- if (warnings.length > 0) {
1576
- addonSpinner.stop("Add-ons applied with warnings.");
1577
- for (const warning of warnings) log.warn(warning);
1578
- return;
1630
+ if (context.shouldInstall) progressSpinner?.message("Installing dependencies...");
1631
+ if (!await installDependenciesForContext(context, projectDir)) {
1632
+ stopProgressOnFailure();
1633
+ return false;
1579
1634
  }
1580
- addonSpinner.stop("Add-ons applied.");
1581
- }
1582
-
1583
- //#endregion
1584
- //#region src/telemetry/client.ts
1585
- const TELEMETRY_API_KEY = "phc_cmc85avbWyuJ2JyKdGPdv7dxXli8xLdWDBPbvIXWJfs";
1586
- const TELEMETRY_HOST = "https://us.i.posthog.com";
1587
- const TELEMETRY_CONFIG_FILE = "telemetry.json";
1588
- const UUID_V4_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
1589
- function isTruthyEnvValue(value) {
1590
- return [
1591
- "1",
1592
- "true",
1593
- "yes",
1594
- "on"
1595
- ].includes(String(value ?? "").trim().toLowerCase());
1596
- }
1597
- function shouldDisableTelemetry() {
1598
- if (isTruthyEnvValue(process.env.CI) || isTruthyEnvValue(process.env.GITHUB_ACTIONS)) return true;
1599
- return process.env.CREATE_PRISMA_DISABLE_TELEMETRY !== void 0 || process.env.CREATE_PRISMA_TELEMETRY_DISABLED !== void 0 || process.env.DO_NOT_TRACK !== void 0;
1600
- }
1601
- function getTelemetryConfigDir() {
1602
- if (process.platform === "darwin") return path.join(os.homedir(), "Library", "Application Support", "create-prisma");
1603
- if (process.platform === "win32") return path.join(process.env.APPDATA ?? path.join(os.homedir(), "AppData", "Roaming"), "create-prisma");
1604
- return path.join(process.env.XDG_CONFIG_HOME ?? path.join(os.homedir(), ".config"), "create-prisma");
1605
- }
1606
- async function getAnonymousId() {
1607
- const telemetryConfigPath = path.join(getTelemetryConfigDir(), TELEMETRY_CONFIG_FILE);
1608
- try {
1609
- const config = await fs.readJSON(telemetryConfigPath);
1610
- if (typeof config.anonymousId === "string" && UUID_V4_REGEX.test(config.anonymousId)) return config.anonymousId;
1611
- } catch {}
1612
- const anonymousId = randomUUID();
1613
- try {
1614
- await fs.ensureDir(path.dirname(telemetryConfigPath));
1615
- await fs.writeJSON(telemetryConfigPath, { anonymousId }, { spaces: 2 });
1616
- } catch {}
1617
- return anonymousId;
1618
- }
1619
- function getCommonProperties() {
1620
- return {
1621
- "cli-version": "0.4.1",
1622
- "node-version": process.version,
1623
- platform: process.platform,
1624
- arch: process.arch
1625
- };
1626
- }
1627
- function sanitizeProperties(properties) {
1628
- return Object.fromEntries(Object.entries(properties).filter(([, value]) => value !== void 0));
1629
- }
1630
- async function trackCliTelemetry(event, properties) {
1631
- if (shouldDisableTelemetry()) return;
1632
- let client;
1633
- try {
1634
- const distinctId = await getAnonymousId();
1635
- const sanitizedProperties = sanitizeProperties({
1636
- ...getCommonProperties(),
1637
- ...properties,
1638
- $process_person_profile: false
1639
- });
1640
- client = new PostHog(TELEMETRY_API_KEY, {
1641
- host: TELEMETRY_HOST,
1642
- disableGeoip: true,
1643
- flushAt: 1,
1644
- flushInterval: 0
1645
- });
1646
- await client.captureImmediate({
1647
- distinctId,
1648
- event,
1649
- properties: sanitizedProperties,
1650
- disableGeoip: true
1651
- });
1652
- } catch {} finally {
1653
- if (client) await client.shutdown().catch(() => {});
1635
+ progressSpinner?.message("Configuring Prisma Next...");
1636
+ if (!await finalizePrismaFilesForContext(context, projectDir, provisionResult)) {
1637
+ stopProgressOnFailure();
1638
+ return false;
1654
1639
  }
1655
- }
1656
-
1657
- //#endregion
1658
- //#region src/telemetry/create.ts
1659
- function getRequestedAddons(input) {
1660
- const addons = [];
1661
- if (input.skills === true) addons.push("skills");
1662
- if (input.mcp === true) addons.push("mcp");
1663
- if (input.extension === true) addons.push("extension");
1664
- return addons;
1665
- }
1666
- function getTargetDirectoryState(context) {
1667
- if (!context.targetPathState.exists) return "new";
1668
- if (context.targetPathState.isEmptyDirectory) return "empty_directory";
1669
- return "non_empty_directory";
1670
- }
1671
- function getBaseCreateProperties(input, context) {
1672
- const resolvedAddons = context?.addonSetupContext?.addons ?? getRequestedAddons(input);
1673
- return {
1674
- command: "create",
1675
- "uses-defaults": input.yes === true,
1676
- verbose: input.verbose === true,
1677
- force: input.force === true,
1678
- template: context?.template ?? input.template ?? null,
1679
- "database-provider": context?.prismaSetupContext.databaseProvider ?? input.provider ?? null,
1680
- "package-manager": context?.prismaSetupContext.packageManager ?? input.packageManager ?? null,
1681
- "schema-preset": context?.prismaSetupContext.schemaPreset ?? input.schemaPreset ?? null,
1682
- "should-install": context?.prismaSetupContext.shouldInstall ?? input.install ?? null,
1683
- "should-generate": context?.prismaSetupContext.shouldGenerate ?? input.generate ?? null,
1684
- "uses-prisma-postgres": context?.prismaSetupContext.shouldUsePrismaPostgres ?? input.prismaPostgres ?? null,
1685
- addons: resolvedAddons,
1686
- "addon-count": resolvedAddons.length,
1687
- "addon-scope": context?.addonSetupContext?.scope ?? null,
1688
- "skills-count": context?.addonSetupContext?.skills.length ?? null,
1689
- "skills-agents-count": context?.addonSetupContext?.skillsAgents.length ?? null,
1690
- "mcp-agents-count": context?.addonSetupContext?.mcpAgents.length ?? null,
1691
- "extension-target-count": context?.addonSetupContext?.extensionTargets.length ?? null,
1692
- "target-directory-state": context ? getTargetDirectoryState(context) : null
1693
- };
1694
- }
1695
- function getErrorName(error) {
1696
- if (error instanceof Error) return error.name;
1697
- return error === void 0 ? null : "UnknownError";
1698
- }
1699
- function getErrorCode(error) {
1700
- if (typeof error !== "object" || error === null) return null;
1701
- const exitCode = Reflect.get(error, "exitCode");
1702
- if (typeof exitCode === "number") return exitCode;
1703
- const code = Reflect.get(error, "code");
1704
- return typeof code === "number" || typeof code === "string" ? code : null;
1705
- }
1706
- async function trackCreateCompleted(params) {
1707
- await trackCliTelemetry("cli:create_command_completed", {
1708
- ...getBaseCreateProperties(params.input, params.context),
1709
- "duration-ms": params.durationMs
1710
- });
1711
- }
1712
- async function trackCreateFailed(params) {
1713
- await trackCliTelemetry("cli:create_command_failed", {
1714
- ...getBaseCreateProperties(params.input, params.context),
1715
- "duration-ms": params.durationMs,
1716
- "failure-stage": params.stage,
1717
- "error-name": getErrorName(params.error),
1718
- "error-code": getErrorCode(params.error)
1640
+ if (context.shouldEmit && context.shouldInstall) progressSpinner?.message("Emitting Prisma Next contract artifacts...");
1641
+ const emitResult = await emitPrismaNextContractForContext(context, projectDir);
1642
+ const warningLines = buildWarningLines(provisionResult.warning, emitResult.warning);
1643
+ const nextSteps = buildNextStepsForContext({
1644
+ context,
1645
+ options,
1646
+ didEmitContract: emitResult.didEmitContract
1719
1647
  });
1648
+ progressSpinner?.stop("Prisma Next project ready.");
1649
+ if (warningLines.length > 0) note(warningLines.map((line) => line.replace(/^- /, "")).join("\n"), "Heads up");
1650
+ note(formatAgentPrompt(), "Agent prompt");
1651
+ if (context.verbose) note(formatNextSteps(nextSteps), "Next steps for Prisma Next");
1652
+ outro("Prisma Next setup complete.");
1653
+ return true;
1720
1654
  }
1721
1655
 
1722
1656
  //#endregion
1723
1657
  //#region src/ui/branding.ts
1724
- const prismaTitle = `${styleText(["bold", "cyan"], "Create")} ${styleText(["bold", "magenta"], "Prisma")}`;
1658
+ const prismaTitle = `${styleText(["bold", "cyanBright"], "◭")} ${styleText(["bold", "cyanBright"], "Create")} ${styleText(["bold", "magentaBright"], "Prisma")} ${styleText(["bold", "blueBright"], "Next")}`;
1725
1659
  function getCreatePrismaIntro() {
1726
1660
  return prismaTitle;
1727
1661
  }
@@ -1729,8 +1663,7 @@ function getCreatePrismaIntro() {
1729
1663
  //#endregion
1730
1664
  //#region src/commands/create.ts
1731
1665
  const DEFAULT_PROJECT_NAME = "my-app";
1732
- const DEFAULT_TEMPLATE = "hono";
1733
- const DEFAULT_SCHEMA_PRESET = "basic";
1666
+ const DEFAULT_TEMPLATE = "minimal";
1734
1667
  function toPackageName(projectName) {
1735
1668
  return projectName.toLowerCase().replace(/[^a-z0-9._-]/g, "-").replace(/^-+/, "").replace(/-+$/, "") || "app";
1736
1669
  }
@@ -1761,50 +1694,50 @@ async function promptForCreateTemplate() {
1761
1694
  message: "Select template",
1762
1695
  initialValue: DEFAULT_TEMPLATE,
1763
1696
  options: [
1697
+ {
1698
+ value: "minimal",
1699
+ label: "Minimal",
1700
+ hint: "Script-first Prisma Next starter with no web framework"
1701
+ },
1764
1702
  {
1765
1703
  value: "hono",
1766
1704
  label: "Hono",
1767
- hint: "TypeScript API starter"
1705
+ hint: "Lightweight TypeScript API server"
1768
1706
  },
1769
1707
  {
1770
1708
  value: "elysia",
1771
1709
  label: "Elysia",
1772
- hint: "TypeScript API starter with Elysia's Node adapter"
1710
+ hint: "Bun-friendly TypeScript API server"
1773
1711
  },
1774
1712
  {
1775
1713
  value: "nest",
1776
1714
  label: "NestJS",
1777
- hint: "Official Nest-style API starter with a Prisma service"
1715
+ hint: "Structured Node API with controllers and services"
1778
1716
  },
1779
1717
  {
1780
1718
  value: "next",
1781
1719
  label: "Next.js",
1782
- hint: "App Router + TypeScript starter"
1720
+ hint: "Full-stack React app with App Router"
1783
1721
  },
1784
1722
  {
1785
1723
  value: "svelte",
1786
1724
  label: "SvelteKit",
1787
- hint: "Official minimal SvelteKit + TypeScript starter"
1725
+ hint: "Full-stack Svelte 5 app with Vite"
1788
1726
  },
1789
1727
  {
1790
1728
  value: "astro",
1791
1729
  label: "Astro",
1792
- hint: "Official minimal Astro starter with API route example"
1730
+ hint: "Content-oriented web app with server routes"
1793
1731
  },
1794
1732
  {
1795
1733
  value: "nuxt",
1796
1734
  label: "Nuxt",
1797
- hint: "Official minimal Nuxt starter with Nitro API route example"
1735
+ hint: "Full-stack Vue app with Nitro server routes"
1798
1736
  },
1799
1737
  {
1800
1738
  value: "tanstack-start",
1801
1739
  label: "TanStack Start",
1802
- hint: "TanStack Start React app with file routes and server functions"
1803
- },
1804
- {
1805
- value: "turborepo",
1806
- label: "Turborepo",
1807
- hint: "Monorepo starter with apps + packages/db Prisma package"
1740
+ hint: "React app with file routes and server functions"
1808
1741
  }
1809
1742
  ]
1810
1743
  });
@@ -1872,8 +1805,8 @@ async function runCreateCommand(rawInput = {}) {
1872
1805
  }
1873
1806
  }
1874
1807
  async function collectCreateContext(input) {
1875
- const useDefaults = input.yes === true;
1876
1808
  const force = input.force === true;
1809
+ const useDefaults = input.yes === true;
1877
1810
  const projectNameInput = input.name ?? (useDefaults ? DEFAULT_PROJECT_NAME : await promptForProjectName());
1878
1811
  if (projectNameInput === void 0) return;
1879
1812
  const projectName = String(projectNameInput).trim();
@@ -1894,42 +1827,33 @@ async function collectCreateContext(input) {
1894
1827
  cancel(`Target directory ${formatPathForDisplay(targetDirectory)} is not empty. Use --force to continue.`);
1895
1828
  return;
1896
1829
  }
1897
- const prismaSetupContext = await collectPrismaSetupContext(input, {
1898
- projectDir: targetDirectory,
1899
- defaultSchemaPreset: DEFAULT_SCHEMA_PRESET
1900
- });
1830
+ const prismaSetupContext = await collectPrismaSetupContext(input, { projectDir: targetDirectory });
1901
1831
  if (!prismaSetupContext) return;
1902
- const addonSetupContext = await collectCreateAddonSetupContext(input, {
1903
- useDefaults,
1904
- provider: prismaSetupContext.databaseProvider,
1905
- shouldUsePrismaPostgres: prismaSetupContext.shouldUsePrismaPostgres
1906
- });
1907
- if (addonSetupContext === void 0) return;
1908
1832
  return {
1909
1833
  targetDirectory,
1910
1834
  targetPathState,
1911
1835
  force,
1912
1836
  template,
1913
1837
  projectPackageName: toPackageName(path.basename(targetDirectory)),
1914
- prismaSetupContext,
1915
- addonSetupContext: addonSetupContext ?? void 0
1838
+ prismaSetupContext
1916
1839
  };
1917
1840
  }
1918
1841
  async function executeCreateContext(context) {
1919
- const scaffoldSpinner = spinner();
1920
- scaffoldSpinner.start(`Scaffolding ${context.template} project...`);
1842
+ const createSpinner = context.prismaSetupContext.verbose ? void 0 : spinner();
1843
+ createSpinner?.start("Creating Prisma Next project...");
1921
1844
  try {
1845
+ if (context.prismaSetupContext.verbose) log.step(`Scaffolding ${context.template} starter.`);
1922
1846
  await scaffoldCreateTemplate({
1923
1847
  projectDir: context.targetDirectory,
1924
1848
  projectName: context.projectPackageName,
1925
1849
  template: context.template,
1926
- schemaPreset: context.prismaSetupContext.schemaPreset,
1927
1850
  provider: context.prismaSetupContext.databaseProvider,
1851
+ authoring: context.prismaSetupContext.authoring,
1928
1852
  packageManager: context.prismaSetupContext.packageManager
1929
1853
  });
1930
- scaffoldSpinner.stop("Project files scaffolded.");
1854
+ if (context.prismaSetupContext.verbose) log.success("Starter files scaffolded.");
1931
1855
  } catch (error) {
1932
- scaffoldSpinner.stop("Could not scaffold project files.");
1856
+ createSpinner?.stop("Could not create Prisma Next project.");
1933
1857
  return {
1934
1858
  ok: false,
1935
1859
  stage: "scaffold_template",
@@ -1940,9 +1864,11 @@ async function executeCreateContext(context) {
1940
1864
  await writeCreateTemplateDependencies({
1941
1865
  template: context.template,
1942
1866
  packageManager: context.prismaSetupContext.packageManager,
1943
- projectDir: context.targetDirectory
1867
+ projectDir: context.targetDirectory,
1868
+ prismaNextSpec: context.prismaSetupContext.prismaNextSpec
1944
1869
  });
1945
1870
  } catch (error) {
1871
+ createSpinner?.stop("Could not create Prisma Next project.");
1946
1872
  return {
1947
1873
  ok: false,
1948
1874
  stage: "scaffold_template",
@@ -1950,31 +1876,22 @@ async function executeCreateContext(context) {
1950
1876
  };
1951
1877
  }
1952
1878
  if (context.targetPathState.exists && !context.targetPathState.isEmptyDirectory && context.force) log.warn(`Used --force in non-empty directory ${formatPathForDisplay(context.targetDirectory)}.`);
1953
- const nextSteps = formatPathForDisplay(context.targetDirectory) === "." ? [] : [`- cd ${formatPathForDisplay(context.targetDirectory)}`];
1954
- if (context.addonSetupContext) try {
1955
- await executeCreateAddonSetupContext({
1956
- context: context.addonSetupContext,
1957
- packageManager: context.prismaSetupContext.packageManager,
1958
- projectDir: context.targetDirectory,
1959
- verbose: context.prismaSetupContext.verbose
1960
- });
1961
- } catch (error) {
1962
- return {
1963
- ok: false,
1964
- stage: "addons",
1965
- error
1966
- };
1967
- }
1879
+ const nextSteps = formatPathForDisplay(context.targetDirectory) === "." ? [] : [{
1880
+ command: `cd ${formatPathForDisplay(context.targetDirectory)}`,
1881
+ description: "Enter your new project directory."
1882
+ }];
1968
1883
  try {
1969
1884
  if (!await executePrismaSetupContext(context.prismaSetupContext, {
1970
1885
  prependNextSteps: nextSteps,
1971
1886
  projectDir: context.targetDirectory,
1972
- includeDevNextStep: true
1887
+ includeDevNextStep: true,
1888
+ progressSpinner: createSpinner
1973
1889
  })) return {
1974
1890
  ok: false,
1975
1891
  stage: "prisma_setup"
1976
1892
  };
1977
1893
  } catch (error) {
1894
+ createSpinner?.stop("Could not create Prisma Next project.");
1978
1895
  return {
1979
1896
  ok: false,
1980
1897
  stage: "prisma_setup",
@@ -1985,4 +1902,4 @@ async function executeCreateContext(context) {
1985
1902
  }
1986
1903
 
1987
1904
  //#endregion
1988
- export { DatabaseUrlSchema as a, DatabaseProviderSchema as i, CreateCommandInputSchema as n, PackageManagerSchema as o, CreateTemplateSchema as r, SchemaPresetSchema as s, runCreateCommand as t };
1905
+ export { DatabaseProviderSchema as a, CreateTemplateSchema as i, AuthoringStyleSchema as n, DatabaseUrlSchema as o, CreateCommandInputSchema as r, PackageManagerSchema as s, runCreateCommand as t };