create-prisma 0.4.1 → 0.4.2-next.37.102.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 +50 -44
  2. package/dist/cli.mjs +1 -1
  3. package/dist/{create-H6Tk0JlE.mjs → create-DsDWZbPE.mjs} +875 -1010
  4. package/dist/index.d.mts +30 -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,75 +1,149 @@
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
- //#region src/utils/runtime.ts
16
- function usesNodeStyleRuntime(packageManager) {
17
- return packageManager !== void 0 && packageManager !== "bun" && packageManager !== "deno";
15
+ //#region src/telemetry/client.ts
16
+ const TELEMETRY_API_KEY = "phc_cmc85avbWyuJ2JyKdGPdv7dxXli8xLdWDBPbvIXWJfs";
17
+ const TELEMETRY_HOST = "https://us.i.posthog.com";
18
+ const TELEMETRY_CONFIG_FILE = "telemetry.json";
19
+ 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;
20
+ function isTruthyEnvValue(value) {
21
+ return [
22
+ "1",
23
+ "true",
24
+ "yes",
25
+ "on"
26
+ ].includes(String(value ?? "").trim().toLowerCase());
18
27
  }
19
- function requiresDotenvConfigImport(packageManager) {
20
- return usesNodeStyleRuntime(packageManager);
28
+ function shouldDisableTelemetry() {
29
+ if (isTruthyEnvValue(process.env.CI) || isTruthyEnvValue(process.env.GITHUB_ACTIONS)) return true;
30
+ return process.env.CREATE_PRISMA_DISABLE_TELEMETRY !== void 0 || process.env.CREATE_PRISMA_TELEMETRY_DISABLED !== void 0 || process.env.DO_NOT_TRACK !== void 0;
21
31
  }
22
-
23
- //#endregion
24
- //#region src/constants/dependencies.ts
25
- const dependencyVersionMap = {
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",
36
- tsx: "^4.21.0"
37
- };
38
- function getWorkspaceDependencyVersion(packageManager) {
39
- return packageManager === "npm" ? "*" : "workspace:*";
32
+ function getTelemetryConfigDir() {
33
+ if (process.platform === "darwin") return path.join(os.homedir(), "Library", "Application Support", "create-prisma");
34
+ if (process.platform === "win32") return path.join(process.env.APPDATA ?? path.join(os.homedir(), "AppData", "Roaming"), "create-prisma");
35
+ return path.join(process.env.XDG_CONFIG_HOME ?? path.join(os.homedir(), ".config"), "create-prisma");
40
36
  }
41
- function getCreateTemplateDependencies(template, packageManager) {
42
- const targets = [];
43
- if (template === "hono" || template === "elysia" || template === "nest") {
44
- const runtimeDevDependencies = usesNodeStyleRuntime(packageManager) ? ["tsx"] : [];
45
- if (template === "elysia" && packageManager !== "deno") targets.push({
46
- packageJsonPath: "package.json",
47
- dependencies: ["@elysiajs/node"],
48
- devDependencies: ["@types/node", ...runtimeDevDependencies]
37
+ async function getAnonymousId() {
38
+ const telemetryConfigPath = path.join(getTelemetryConfigDir(), TELEMETRY_CONFIG_FILE);
39
+ try {
40
+ const config = await fs.readJSON(telemetryConfigPath);
41
+ if (typeof config.anonymousId === "string" && UUID_V4_REGEX.test(config.anonymousId)) return config.anonymousId;
42
+ } catch {}
43
+ const anonymousId = randomUUID();
44
+ try {
45
+ await fs.ensureDir(path.dirname(telemetryConfigPath));
46
+ await fs.writeJSON(telemetryConfigPath, { anonymousId }, { spaces: 2 });
47
+ } catch {}
48
+ return anonymousId;
49
+ }
50
+ function getCommonProperties() {
51
+ return {
52
+ "cli-version": "0.4.2-next.37.102.1",
53
+ "node-version": process.version,
54
+ platform: process.platform,
55
+ arch: process.arch
56
+ };
57
+ }
58
+ function sanitizeProperties(properties) {
59
+ return Object.fromEntries(Object.entries(properties).filter(([, value]) => value !== void 0));
60
+ }
61
+ async function trackCliTelemetry(event, properties) {
62
+ if (shouldDisableTelemetry()) return;
63
+ let client;
64
+ try {
65
+ const distinctId = await getAnonymousId();
66
+ const sanitizedProperties = sanitizeProperties({
67
+ ...getCommonProperties(),
68
+ ...properties,
69
+ $process_person_profile: false
49
70
  });
50
- else if (runtimeDevDependencies.length > 0) targets.push({
51
- packageJsonPath: "package.json",
52
- dependencies: [],
53
- devDependencies: runtimeDevDependencies
71
+ client = new PostHog(TELEMETRY_API_KEY, {
72
+ host: TELEMETRY_HOST,
73
+ disableGeoip: true,
74
+ flushAt: 1,
75
+ flushInterval: 0
76
+ });
77
+ await client.captureImmediate({
78
+ distinctId,
79
+ event,
80
+ properties: sanitizedProperties,
81
+ disableGeoip: true
54
82
  });
83
+ } catch {} finally {
84
+ if (client) await client.shutdown().catch(() => {});
55
85
  }
56
- if (template === "turborepo") targets.push({
57
- packageJsonPath: "apps/api/package.json",
58
- dependencies: [],
59
- devDependencies: ["tsx"],
60
- customDependencies: { "@repo/db": getWorkspaceDependencyVersion(packageManager) }
86
+ }
87
+
88
+ //#endregion
89
+ //#region src/telemetry/create.ts
90
+ const CREATE_PRISMA_NEXT_COMPLETED_EVENT = "cli:create_prisma_next_command_completed";
91
+ const CREATE_PRISMA_NEXT_FAILED_EVENT = "cli:create_prisma_next_command_failed";
92
+ function getTargetDirectoryState(context) {
93
+ if (!context.targetPathState.exists) return "new";
94
+ if (context.targetPathState.isEmptyDirectory) return "empty_directory";
95
+ return "non_empty_directory";
96
+ }
97
+ function getBaseCreateProperties(input, context) {
98
+ return {
99
+ command: "create",
100
+ "uses-defaults": input.yes === true,
101
+ verbose: input.verbose === true,
102
+ force: input.force === true,
103
+ template: context?.template ?? input.template ?? null,
104
+ "database-provider": context?.prismaSetupContext.databaseProvider ?? input.provider ?? null,
105
+ "authoring-style": context?.prismaSetupContext.authoring ?? input.authoring ?? null,
106
+ "package-manager": context?.prismaSetupContext.packageManager ?? input.packageManager ?? null,
107
+ "should-install": context?.prismaSetupContext.shouldInstall ?? input.install ?? null,
108
+ "should-emit": context?.prismaSetupContext.shouldEmit ?? input.emit ?? null,
109
+ "uses-prisma-postgres": context?.prismaSetupContext.shouldUsePrismaPostgres ?? input.prismaPostgres ?? null,
110
+ "target-directory-state": context ? getTargetDirectoryState(context) : null
111
+ };
112
+ }
113
+ function getErrorName(error) {
114
+ if (error instanceof Error) return error.name;
115
+ return error === void 0 ? null : "UnknownError";
116
+ }
117
+ function getErrorCode(error) {
118
+ if (typeof error !== "object" || error === null) return null;
119
+ const exitCode = Reflect.get(error, "exitCode");
120
+ if (typeof exitCode === "number") return exitCode;
121
+ const code = Reflect.get(error, "code");
122
+ return typeof code === "number" || typeof code === "string" ? code : null;
123
+ }
124
+ async function trackCreateCompleted(params) {
125
+ await trackCliTelemetry(CREATE_PRISMA_NEXT_COMPLETED_EVENT, {
126
+ ...getBaseCreateProperties(params.input, params.context),
127
+ "duration-ms": params.durationMs
128
+ });
129
+ }
130
+ async function trackCreateFailed(params) {
131
+ await trackCliTelemetry(CREATE_PRISMA_NEXT_FAILED_EVENT, {
132
+ ...getBaseCreateProperties(params.input, params.context),
133
+ "duration-ms": params.durationMs,
134
+ "failure-stage": params.stage,
135
+ "error-name": getErrorName(params.error),
136
+ "error-code": getErrorCode(params.error)
61
137
  });
62
- return targets;
63
138
  }
64
139
 
65
140
  //#endregion
66
141
  //#region src/types.ts
67
- const databaseProviders = [
142
+ const databaseProviderInputs = [
143
+ "postgres",
68
144
  "postgresql",
69
- "mysql",
70
- "sqlite",
71
- "sqlserver",
72
- "cockroachdb"
145
+ "mongo",
146
+ "mongodb"
73
147
  ];
74
148
  const packageManagers = [
75
149
  "npm",
@@ -78,8 +152,9 @@ const packageManagers = [
78
152
  "bun",
79
153
  "deno"
80
154
  ];
81
- const schemaPresets = ["empty", "basic"];
155
+ const authoringStyles = ["psl", "typescript"];
82
156
  const createTemplates = [
157
+ "minimal",
83
158
  "hono",
84
159
  "elysia",
85
160
  "nest",
@@ -87,56 +162,35 @@ const createTemplates = [
87
162
  "svelte",
88
163
  "astro",
89
164
  "nuxt",
90
- "tanstack-start",
91
- "turborepo"
165
+ "tanstack-start"
92
166
  ];
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);
167
+ function normalizeDatabaseProvider(value) {
168
+ if (value === "postgresql") return "postgres";
169
+ if (value === "mongodb") return "mongo";
170
+ return value;
171
+ }
172
+ const DatabaseProviderSchema = z.enum(databaseProviderInputs).transform(normalizeDatabaseProvider);
112
173
  const PackageManagerSchema = z.enum(packageManagers);
113
- const SchemaPresetSchema = z.enum(schemaPresets);
174
+ const AuthoringStyleSchema = z.enum(authoringStyles);
114
175
  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
176
  const DatabaseUrlSchema = z.string().trim().min(1, "Please enter a valid database URL");
120
177
  const CommonCommandOptionsSchema = z.object({
121
178
  yes: z.boolean().optional().describe("Skip prompts and accept default choices"),
122
179
  verbose: z.boolean().optional().describe("Show verbose command output during setup")
123
180
  });
124
181
  const PrismaSetupOptionsSchema = z.object({
125
- provider: DatabaseProviderSchema.optional().describe("Database provider"),
182
+ provider: DatabaseProviderSchema.optional().describe("Prisma Next database target: PostgreSQL relational models or MongoDB document models"),
183
+ authoring: AuthoringStyleSchema.optional().describe("Contract authoring style"),
126
184
  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"),
185
+ prismaPostgres: z.boolean().optional().describe("Provision Prisma Postgres with create-db when target is postgres"),
128
186
  databaseUrl: DatabaseUrlSchema.optional().describe("DATABASE_URL value"),
129
187
  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")
188
+ emit: z.boolean().optional().describe("Emit Prisma Next contract artifacts after scaffolding")
132
189
  });
133
190
  const PrismaSetupCommandInputSchema = CommonCommandOptionsSchema.extend(PrismaSetupOptionsSchema.shape);
134
191
  const CreateScaffoldOptionsSchema = z.object({
135
192
  name: z.string().trim().min(1, "Please enter a valid project name").optional().describe("Project name / directory"),
136
193
  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
194
  force: z.boolean().optional().describe("Allow scaffolding into a non-empty target directory")
141
195
  });
142
196
  const CreateCommandInputSchema = PrismaSetupCommandInputSchema.extend(CreateScaffoldOptionsSchema.shape);
@@ -221,13 +275,17 @@ function getPackageManagerManifestValue(packageManager) {
221
275
  return packageManagerManifestValues[packageManager];
222
276
  }
223
277
  function getDenoPrismaSpecifier() {
224
- return `npm:prisma@${dependencyVersionMap.prisma}`;
278
+ return "npm:prisma-next";
279
+ }
280
+ function getDenoNpmSpecifier(packageSpecifier) {
281
+ return `npm:${packageSpecifier.replace(/@latest$/, "")}`;
225
282
  }
226
283
  function getDenoAllowedScriptSpecifiers() {
227
284
  return [
228
- `npm:prisma@${dependencyVersionMap.prisma}`,
229
- `npm:@prisma/client@${dependencyVersionMap["@prisma/client"]}`,
230
- `npm:@prisma/engines@${dependencyVersionMap.prisma}`
285
+ "npm:prisma-next",
286
+ "npm:@prisma-next/postgres",
287
+ "npm:@prisma-next/mongo",
288
+ "npm:mongodb-memory-server"
231
289
  ].join(",");
232
290
  }
233
291
  function getInstallCommand(packageManager) {
@@ -311,7 +369,7 @@ function getPackageExecutionArgs(packageManager, commandArgs) {
311
369
  args: [
312
370
  "run",
313
371
  "-A",
314
- `npm:${packageName}`,
372
+ getDenoNpmSpecifier(packageName),
315
373
  ...args
316
374
  ]
317
375
  };
@@ -326,29 +384,58 @@ function getPackageExecutionCommand(packageManager, commandArgs) {
326
384
  const execution = getPackageExecutionArgs(packageManager, commandArgs);
327
385
  return [execution.command, ...execution.args].join(" ");
328
386
  }
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]);
387
+ function getLocalPackageBinaryArgs(packageManager, binaryName, binaryArgs) {
388
+ switch (packageManager) {
389
+ case "pnpm": return {
390
+ command: "pnpm",
391
+ args: [
392
+ "exec",
393
+ binaryName,
394
+ ...binaryArgs
395
+ ]
396
+ };
397
+ case "yarn": return {
398
+ command: "yarn",
399
+ args: [binaryName, ...binaryArgs]
400
+ };
401
+ case "bun": return {
402
+ command: "bun",
403
+ args: [binaryName, ...binaryArgs]
404
+ };
405
+ case "deno": return {
406
+ command: "deno",
407
+ args: [
408
+ "run",
409
+ "-A",
410
+ `npm:${binaryName}`,
411
+ ...binaryArgs
412
+ ]
413
+ };
414
+ default: return {
415
+ command: "npm",
416
+ args: [
417
+ "exec",
418
+ binaryName,
419
+ "--",
420
+ ...binaryArgs
421
+ ]
422
+ };
423
+ }
346
424
  }
347
- function getPrismaCliCommand(packageManager, prismaArgs) {
348
- const execution = getPrismaCliArgs(packageManager, prismaArgs);
425
+ function getLocalPackageBinaryCommand(packageManager, binaryName, binaryArgs) {
426
+ const execution = getLocalPackageBinaryArgs(packageManager, binaryName, binaryArgs);
349
427
  return [execution.command, ...execution.args].join(" ");
350
428
  }
351
429
 
430
+ //#endregion
431
+ //#region src/utils/runtime.ts
432
+ function usesNodeStyleRuntime(packageManager) {
433
+ return packageManager !== void 0 && packageManager !== "bun" && packageManager !== "deno";
434
+ }
435
+ function requiresDotenvConfigImport(packageManager) {
436
+ return usesNodeStyleRuntime(packageManager);
437
+ }
438
+
352
439
  //#endregion
353
440
  //#region src/templates/shared.ts
354
441
  function getOptionalHashString(hash, key) {
@@ -431,94 +518,176 @@ async function renderTemplateTree(opts) {
431
518
  function getCreateTemplateDir(template) {
432
519
  return resolveTemplatesDir(`templates/create/${template}`);
433
520
  }
434
- function createTemplateContext(projectName, provider, schemaPreset, packageManager) {
521
+ function getCreateSharedTemplateDir() {
522
+ return resolveTemplatesDir("templates/create/_shared");
523
+ }
524
+ function createTemplateContext(projectName, template, provider, authoring, packageManager) {
435
525
  return {
436
526
  projectName,
527
+ template,
437
528
  provider,
438
- schemaPreset,
529
+ authoring,
439
530
  packageManager
440
531
  };
441
532
  }
442
533
  async function scaffoldCreateTemplate(opts) {
443
- const { projectDir, projectName, template, provider, schemaPreset, packageManager } = opts;
534
+ const { projectDir, projectName, template, provider, authoring, packageManager } = opts;
535
+ const templateRoot = getCreateTemplateDir(template);
536
+ const sharedTemplateRoot = getCreateSharedTemplateDir();
537
+ const context = createTemplateContext(projectName, template, provider, authoring, packageManager);
538
+ await renderTemplateTree({
539
+ templateRoot: sharedTemplateRoot,
540
+ outputDir: projectDir,
541
+ context
542
+ });
444
543
  await renderTemplateTree({
445
- templateRoot: getCreateTemplateDir(template),
544
+ templateRoot,
446
545
  outputDir: projectDir,
447
- context: createTemplateContext(projectName, provider, schemaPreset, packageManager)
546
+ context
547
+ });
548
+ }
549
+
550
+ //#endregion
551
+ //#region src/constants/dependencies.ts
552
+ const dependencyVersionMap = {
553
+ "@elysiajs/node": "^1.4.5",
554
+ "@types/node": "^25.6.2",
555
+ dotenv: "^17.4.2",
556
+ "mongodb-memory-server": "^11.1.0",
557
+ tsx: "^4.21.0"
558
+ };
559
+ const PRISMA_NEXT_PACKAGE_VERSION = "latest";
560
+ function isPrismaNextPackage(packageName) {
561
+ return packageName === "prisma-next" || packageName.startsWith("@prisma-next/");
562
+ }
563
+ function getPrismaNextPackageSpecifier(packageName) {
564
+ return `${packageName}@${PRISMA_NEXT_PACKAGE_VERSION}`;
565
+ }
566
+ function getDependencyVersion(packageName) {
567
+ if (isPrismaNextPackage(packageName)) return PRISMA_NEXT_PACKAGE_VERSION;
568
+ return dependencyVersionMap[packageName];
569
+ }
570
+ function usesViteDevServer(template) {
571
+ return template === "astro" || template === "nuxt" || template === "svelte" || template === "tanstack-start";
572
+ }
573
+ function getCreateTemplateDependencies(template, packageManager) {
574
+ const targets = [];
575
+ if (usesViteDevServer(template)) targets.push({
576
+ packageJsonPath: "package.json",
577
+ dependencies: [],
578
+ devDependencies: ["@prisma-next/vite-plugin-contract-emit"]
448
579
  });
580
+ if (template === "minimal" || template === "hono" || template === "elysia" || template === "nest") {
581
+ const runtimeDevDependencies = usesNodeStyleRuntime(packageManager) ? ["tsx"] : [];
582
+ if (template === "elysia" && packageManager !== "deno") targets.push({
583
+ packageJsonPath: "package.json",
584
+ dependencies: ["@elysiajs/node"],
585
+ devDependencies: ["@types/node", ...runtimeDevDependencies]
586
+ });
587
+ else if (runtimeDevDependencies.length > 0) targets.push({
588
+ packageJsonPath: "package.json",
589
+ dependencies: [],
590
+ devDependencies: runtimeDevDependencies
591
+ });
592
+ }
593
+ return targets;
449
594
  }
450
595
 
451
596
  //#endregion
452
597
  //#region src/constants/db-packages.ts
453
- function getDbPackages(provider) {
598
+ function getDbPackages(provider, _packageManager) {
454
599
  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";
600
+ case "postgres": return "@prisma-next/postgres";
601
+ case "mongo": return "@prisma-next/mongo";
460
602
  default: {
461
603
  const exhaustiveCheck = provider;
462
- throw new Error(`Unsupported database provider: ${String(exhaustiveCheck)}`);
604
+ throw new Error(`Unsupported Prisma Next target: ${String(exhaustiveCheck)}`);
463
605
  }
464
606
  }
465
607
  }
466
608
 
467
609
  //#endregion
468
610
  //#region src/tasks/install.ts
469
- function getPrismaScriptMap(packageManager) {
611
+ function getPrismaNextScriptMap(packageManager) {
470
612
  if (packageManager === "deno") {
471
- const prismaSpecifier = getDenoPrismaSpecifier();
613
+ const prismaNextCli = `deno run -A --env-file=.env ${getDenoPrismaSpecifier()}`;
472
614
  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`
615
+ "contract:emit": `${prismaNextCli} contract emit`,
616
+ "db:init": `${prismaNextCli} db init`,
617
+ "db:update": `${prismaNextCli} db update`,
618
+ "db:verify": `${prismaNextCli} db verify`,
619
+ "db:seed": "deno run -A --env-file=.env prisma/seed.ts",
620
+ "migration:plan": `${prismaNextCli} migration plan`,
621
+ migrate: `${prismaNextCli} migrate`,
622
+ "migration:status": `${prismaNextCli} migration status`,
623
+ "migration:show": `${prismaNextCli} migration show`
477
624
  };
478
625
  }
479
626
  if (packageManager === "bun") {
480
- const prismaCli = "bun --env-file=.env ./node_modules/.bin/prisma";
627
+ const prismaNextCli = "bun prisma-next";
481
628
  return {
482
- "db:generate": `${prismaCli} generate`,
483
- "db:push": `${prismaCli} db push`,
484
- "db:migrate": `${prismaCli} migrate dev`,
485
- "db:seed": `${prismaCli} db seed`
629
+ "contract:emit": `${prismaNextCli} contract emit`,
630
+ "db:init": `${prismaNextCli} db init`,
631
+ "db:update": `${prismaNextCli} db update`,
632
+ "db:verify": `${prismaNextCli} db verify`,
633
+ "db:seed": "bun prisma/seed.ts",
634
+ "migration:plan": `${prismaNextCli} migration plan`,
635
+ migrate: `${prismaNextCli} migrate`,
636
+ "migration:status": `${prismaNextCli} migration status`,
637
+ "migration:show": `${prismaNextCli} migration show`
486
638
  };
487
639
  }
488
640
  return {
489
- "db:generate": "prisma generate",
490
- "db:push": "prisma db push",
491
- "db:migrate": "prisma migrate dev",
492
- "db:seed": "prisma db seed"
641
+ "contract:emit": "prisma-next contract emit",
642
+ "db:init": "prisma-next db init",
643
+ "db:update": "prisma-next db update",
644
+ "db:verify": "prisma-next db verify",
645
+ "db:seed": "tsx prisma/seed.ts",
646
+ "migration:plan": "prisma-next migration plan",
647
+ migrate: "prisma-next migrate",
648
+ "migration:status": "prisma-next migration status",
649
+ "migration:show": "prisma-next migration show"
493
650
  };
494
651
  }
495
- function getVersion(packageName) {
496
- return dependencyVersionMap[packageName];
497
- }
498
652
  function unique(items) {
499
653
  return [...new Set(items)];
500
654
  }
501
655
  function sortRecord(record) {
502
656
  return Object.fromEntries(Object.entries(record).sort(([a], [b]) => a.localeCompare(b)));
503
657
  }
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;
658
+ function getGeneratedContractTypePackages(provider) {
659
+ if (provider === "mongo") return [
660
+ "@prisma-next/adapter-mongo",
661
+ "@prisma-next/contract",
662
+ "@prisma-next/mongo-contract"
663
+ ];
664
+ return [
665
+ "@prisma-next/adapter-postgres",
666
+ "@prisma-next/contract",
667
+ "@prisma-next/sql-contract",
668
+ "@prisma-next/target-postgres"
669
+ ];
670
+ }
671
+ function getTypeScriptContractPackages(provider) {
672
+ if (provider === "mongo") return [
673
+ ...getGeneratedContractTypePackages(provider),
674
+ "@prisma-next/family-mongo",
675
+ "@prisma-next/mongo-contract-ts",
676
+ "@prisma-next/target-mongo"
677
+ ];
678
+ return [
679
+ ...getGeneratedContractTypePackages(provider),
680
+ "@prisma-next/family-sql",
681
+ "@prisma-next/sql-contract-ts"
682
+ ];
683
+ }
684
+ function getMigrationPackages(provider) {
685
+ if (provider === "mongo") return ["@prisma-next/family-mongo", "@prisma-next/target-mongo"];
686
+ return ["@prisma-next/target-postgres"];
687
+ }
688
+ function getOrmTypePackages(provider) {
689
+ if (provider === "mongo") return ["@prisma-next/mongo-orm"];
690
+ return ["@prisma-next/sql-orm-client"];
522
691
  }
523
692
  async function addPackageDependency(opts) {
524
693
  const { dependencies = [], devDependencies = [], customDependencies = {}, scripts = {}, scriptMode, projectDir } = opts;
@@ -529,12 +698,12 @@ async function addPackageDependency(opts) {
529
698
  if (!pkgJson.devDependencies) pkgJson.devDependencies = {};
530
699
  if (!pkgJson.scripts) pkgJson.scripts = {};
531
700
  for (const pkgName of unique(dependencies)) {
532
- const version = getVersion(pkgName);
701
+ const version = getDependencyVersion(pkgName);
533
702
  if (version) pkgJson.dependencies[pkgName] = version;
534
703
  else console.warn(`Warning: Dependency ${pkgName} not found in version map.`);
535
704
  }
536
705
  for (const pkgName of unique(devDependencies)) {
537
- const version = getVersion(pkgName);
706
+ const version = getDependencyVersion(pkgName);
538
707
  if (version) pkgJson.devDependencies[pkgName] = version;
539
708
  else console.warn(`Warning: Dev dependency ${pkgName} not found in version map.`);
540
709
  }
@@ -550,17 +719,21 @@ async function addPackageDependency(opts) {
550
719
  pkgJson.devDependencies = sortRecord(pkgJson.devDependencies);
551
720
  await fs.writeJson(pkgJsonPath, pkgJson, { spaces: 2 });
552
721
  }
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");
722
+ async function writePrismaDependencies(provider, packageManager, authoring, projectDir = process.cwd()) {
723
+ const dependencies = [getDbPackages(provider, packageManager), "dotenv"];
724
+ const devDependencies = [
725
+ "prisma-next",
726
+ "@prisma-next/cli",
727
+ "@types/node"
728
+ ];
729
+ devDependencies.push(...getGeneratedContractTypePackages(provider));
730
+ devDependencies.push(...getMigrationPackages(provider));
731
+ devDependencies.push(...getOrmTypePackages(provider));
732
+ if (authoring === "typescript") devDependencies.push(...getTypeScriptContractPackages(provider));
559
733
  await addPackageDependency({
560
734
  dependencies,
561
735
  devDependencies,
562
- scripts: getPrismaScriptMap(packageManager),
563
- scriptMode: "if-missing",
736
+ scripts: getPrismaNextScriptMap(packageManager),
564
737
  projectDir
565
738
  });
566
739
  }
@@ -580,8 +753,10 @@ async function writeCreateTemplateDependencies(opts) {
580
753
  async function installProjectDependencies(packageManager, projectDir = process.cwd(), options = {}) {
581
754
  const verbose = options.verbose === true;
582
755
  const installCommand = getInstallArgs(packageManager);
756
+ const env = packageManager === "yarn" ? { YARN_ENABLE_IMMUTABLE_INSTALLS: "false" } : void 0;
583
757
  await execa(installCommand.command, installCommand.args, {
584
758
  cwd: projectDir,
759
+ env,
585
760
  stdio: verbose ? "inherit" : "pipe"
586
761
  });
587
762
  }
@@ -643,55 +818,233 @@ function getCreateDbCommand(packageManager) {
643
818
 
644
819
  //#endregion
645
820
  //#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;
821
+ const DEFAULT_DATABASE_PROVIDER = "postgres";
822
+ const DEFAULT_AUTHORING = "psl";
649
823
  const DEFAULT_INSTALL = true;
650
- const DEFAULT_GENERATE = true;
824
+ const DEFAULT_EMIT = true;
825
+ const DEFAULT_INTERACTIVE_PRISMA_POSTGRES = true;
826
+ const DEFAULT_AUTOMATED_PRISMA_POSTGRES = false;
827
+ const MONGO_MEMORY_SERVER_SCRIPT = `import { spawn } from "node:child_process";
828
+ import { closeSync, existsSync, mkdirSync, openSync, readFileSync, rmSync, writeFileSync } from "node:fs";
829
+ import path from "node:path";
830
+
831
+ const defaultDatabaseUrl = "mongodb://localhost:27017/mydb?replicaSet=rs0&directConnection=true";
832
+ const dataRoot = path.resolve(process.env.MONGO_DB_PATH ?? ".mongo-data");
833
+ const dbPath = path.join(dataRoot, "db");
834
+ const pidFile = path.join(dataRoot, "mongo.pid");
835
+ const logFile = path.join(dataRoot, "mongo.log");
836
+ const readyTimeoutMs = Number(process.env.MONGO_READY_TIMEOUT_MS ?? 60_000);
837
+
838
+ function getMongoConfig() {
839
+ const databaseUrl = process.env.DATABASE_URL ?? defaultDatabaseUrl;
840
+ const url = new URL(databaseUrl);
841
+ if (url.protocol !== "mongodb:") {
842
+ throw new Error("DATABASE_URL must use the mongodb:// protocol.");
843
+ }
844
+
845
+ const port = Number(url.port || "27017");
846
+ if (!Number.isInteger(port) || port < 1 || port > 65535) {
847
+ throw new Error(\`DATABASE_URL has an invalid MongoDB port: \${url.port}\`);
848
+ }
849
+
850
+ return {
851
+ databaseUrl,
852
+ port,
853
+ replSetName: url.searchParams.get("replicaSet") || "rs0",
854
+ };
855
+ }
856
+
857
+ function readPid() {
858
+ if (!existsSync(pidFile)) return null;
859
+ const raw = readFileSync(pidFile, "utf8").trim();
860
+ const pid = Number(raw);
861
+ return Number.isFinite(pid) && pid > 0 ? pid : null;
862
+ }
863
+
864
+ function isAlive(pid) {
865
+ try {
866
+ process.kill(pid, 0);
867
+ return true;
868
+ } catch {
869
+ return false;
870
+ }
871
+ }
872
+
873
+ function getChildCommand() {
874
+ const scriptPath = path.resolve(process.argv[1] ?? "");
875
+ const versions = process.versions;
876
+ if (versions.deno) return { command: process.execPath, args: ["run", "-A", scriptPath, "_run"] };
877
+ return { command: process.execPath, args: [scriptPath, "_run"] };
878
+ }
879
+
880
+ async function runServer() {
881
+ mkdirSync(dbPath, { recursive: true });
882
+ const config = getMongoConfig();
883
+ const memoryServer = await import("mongodb-memory-server");
884
+ const { MongoMemoryReplSet } = memoryServer.default ?? memoryServer;
885
+ const replSet = await MongoMemoryReplSet.create({
886
+ replSet: { name: config.replSetName, count: 1 },
887
+ instanceOpts: [{ port: config.port, storageEngine: "wiredTiger", dbPath }],
888
+ });
889
+ console.log(\`MongoDB server ready for \${config.databaseUrl}\`);
890
+ console.log(\`Data directory: \${dbPath}\`);
891
+ const shutdown = async () => {
892
+ await replSet.stop();
893
+ process.exit(0);
894
+ };
895
+ process.on("SIGINT", shutdown);
896
+ process.on("SIGTERM", shutdown);
897
+ }
898
+
899
+ async function up() {
900
+ mkdirSync(dataRoot, { recursive: true });
901
+ const existing = readPid();
902
+ if (existing !== null && isAlive(existing)) {
903
+ console.log(\`MongoDB is already running (PID \${existing}). Use \\\`db:down\\\` to stop.\`);
904
+ return;
905
+ }
906
+ if (existing !== null) rmSync(pidFile, { force: true });
907
+
908
+ writeFileSync(logFile, "");
909
+ const logFd = openSync(logFile, "a");
910
+ const { command, args } = getChildCommand();
911
+ const child = spawn(
912
+ command,
913
+ args,
914
+ {
915
+ detached: true,
916
+ stdio: ["ignore", logFd, logFd],
917
+ env: process.env,
918
+ },
919
+ );
920
+ closeSync(logFd);
921
+ if (typeof child.pid !== "number") throw new Error("Failed to spawn MongoDB child process.");
922
+ writeFileSync(pidFile, String(child.pid));
923
+ child.unref();
924
+
925
+ const start = Date.now();
926
+ while (Date.now() - start < readyTimeoutMs) {
927
+ if (!isAlive(child.pid)) {
928
+ console.error("MongoDB failed to start:");
929
+ console.error(readFileSync(logFile, "utf8"));
930
+ rmSync(pidFile, { force: true });
931
+ process.exit(1);
932
+ }
933
+ const log = readFileSync(logFile, "utf8");
934
+ if (log.includes("MongoDB server ready")) {
935
+ for (const line of log.split("\\n")) {
936
+ if (line.trim().length > 0) console.log(line);
937
+ }
938
+ console.log(\`Detached (PID \${child.pid}). Logs: \${logFile}\`);
939
+ console.log("Stop with \`db:down\` or wipe with \`db:reset\`.");
940
+ return;
941
+ }
942
+ await new Promise((resolve) => setTimeout(resolve, 100));
943
+ }
944
+ console.error(\`Timed out waiting for MongoDB after \${readyTimeoutMs}ms.\`);
945
+ console.error(readFileSync(logFile, "utf8"));
946
+ try {
947
+ process.kill(child.pid, "SIGTERM");
948
+ } catch {
949
+ // ignore
950
+ }
951
+ rmSync(pidFile, { force: true });
952
+ process.exit(1);
953
+ }
954
+
955
+ async function down(wipe) {
956
+ const pid = readPid();
957
+ if (pid !== null && isAlive(pid)) {
958
+ process.kill(pid, "SIGTERM");
959
+ const deadline = Date.now() + 10_000;
960
+ while (Date.now() < deadline && isAlive(pid)) {
961
+ await new Promise((resolve) => setTimeout(resolve, 100));
962
+ }
963
+ if (isAlive(pid)) {
964
+ console.warn(\`MongoDB (PID \${pid}) did not exit within 10s; sending SIGKILL.\`);
965
+ try {
966
+ process.kill(pid, "SIGKILL");
967
+ } catch {
968
+ // ignore
969
+ }
970
+ }
971
+ console.log(\`Stopped MongoDB (PID \${pid}).\`);
972
+ } else if (pid !== null) {
973
+ console.log("MongoDB was not running (stale PID file).");
974
+ } else {
975
+ console.log("MongoDB is not running.");
976
+ }
977
+ rmSync(pidFile, { force: true });
978
+ if (wipe) {
979
+ rmSync(dataRoot, { recursive: true, force: true });
980
+ console.log(\`Removed \${dataRoot}.\`);
981
+ }
982
+ }
983
+
984
+ const cmd = process.argv[2] ?? "up";
985
+ switch (cmd) {
986
+ case "up":
987
+ await up();
988
+ break;
989
+ case "down":
990
+ await down(false);
991
+ break;
992
+ case "reset":
993
+ await down(true);
994
+ break;
995
+ case "_run":
996
+ await runServer();
997
+ break;
998
+ default:
999
+ console.error(\`Unknown command: \${cmd}. Use: up | down | reset\`);
1000
+ process.exit(2);
1001
+ }
1002
+ `;
1003
+ function getMongoMemoryScripts(packageManager) {
1004
+ switch (packageManager) {
1005
+ case "bun": return {
1006
+ "db:up": "bun --env-file=.env scripts/mongo.mjs up",
1007
+ "db:down": "bun --env-file=.env scripts/mongo.mjs down",
1008
+ "db:reset": "bun --env-file=.env scripts/mongo.mjs reset"
1009
+ };
1010
+ case "deno": return {
1011
+ "db:up": "deno run -A --env-file=.env scripts/mongo.mjs up",
1012
+ "db:down": "deno run -A --env-file=.env scripts/mongo.mjs down",
1013
+ "db:reset": "deno run -A --env-file=.env scripts/mongo.mjs reset"
1014
+ };
1015
+ default: return {
1016
+ "db:up": "node --env-file=.env scripts/mongo.mjs up",
1017
+ "db:down": "node --env-file=.env scripts/mongo.mjs down",
1018
+ "db:reset": "node --env-file=.env scripts/mongo.mjs reset"
1019
+ };
1020
+ }
1021
+ }
651
1022
  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"],
1023
+ ["prisma/contract.prisma", "prisma/contract.ts"],
1024
+ ["prisma-next.config.ts"],
655
1025
  [
656
1026
  "src/lib/prisma.ts",
657
1027
  "src/lib/prisma.server.ts",
658
1028
  "src/lib/server/prisma.ts",
659
- "server/utils/prisma.ts",
660
- "packages/db/src/client.ts"
1029
+ "server/utils/prisma.ts"
661
1030
  ]
662
1031
  ];
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;
1032
+ function getContractPath(authoring) {
1033
+ return `prisma/contract${authoring === "typescript" ? ".ts" : ".prisma"}`;
667
1034
  }
668
1035
  async function promptForDatabaseProvider() {
669
1036
  const databaseProvider = await select({
670
1037
  message: "Select your database",
671
1038
  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
- ]
1039
+ options: [{
1040
+ value: "postgres",
1041
+ label: "PostgreSQL",
1042
+ hint: "Relational models with typed ORM, relations, indexes, raw SQL"
1043
+ }, {
1044
+ value: "mongo",
1045
+ label: "MongoDB",
1046
+ hint: "Document models with typed ORM, indexes, aggregations"
1047
+ }]
695
1048
  });
696
1049
  if (isCancel(databaseProvider)) {
697
1050
  cancel("Operation cancelled.");
@@ -699,10 +1052,48 @@ async function promptForDatabaseProvider() {
699
1052
  }
700
1053
  return DatabaseProviderSchema.parse(databaseProvider);
701
1054
  }
1055
+ async function promptForAuthoringStyle() {
1056
+ const authoring = await select({
1057
+ message: "Choose contract authoring style",
1058
+ initialValue: DEFAULT_AUTHORING,
1059
+ options: [{
1060
+ value: "psl",
1061
+ label: "PSL",
1062
+ hint: "Schema syntax emits contract.json + types"
1063
+ }, {
1064
+ value: "typescript",
1065
+ label: "TypeScript",
1066
+ hint: "Builder API emits the same contract artifacts"
1067
+ }]
1068
+ });
1069
+ if (isCancel(authoring)) {
1070
+ cancel("Operation cancelled.");
1071
+ return;
1072
+ }
1073
+ return AuthoringStyleSchema.parse(authoring);
1074
+ }
1075
+ async function promptForPrismaPostgres() {
1076
+ const shouldUsePrismaPostgres = await confirm({
1077
+ message: "Provision a Prisma Postgres database?",
1078
+ active: "Provision Prisma Postgres",
1079
+ inactive: "Use my own database",
1080
+ initialValue: DEFAULT_INTERACTIVE_PRISMA_POSTGRES
1081
+ });
1082
+ if (isCancel(shouldUsePrismaPostgres)) {
1083
+ cancel("Operation cancelled.");
1084
+ return;
1085
+ }
1086
+ return Boolean(shouldUsePrismaPostgres);
1087
+ }
702
1088
  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";
1089
+ const hint = {
1090
+ npm: "Node.js default",
1091
+ pnpm: "Fast, disk-efficient Node.js package manager",
1092
+ yarn: "Yarn package manager",
1093
+ bun: "Fast runtime + package manager",
1094
+ deno: "Deno runtime + task runner"
1095
+ }[option];
1096
+ return option === detected ? `Detected; ${hint}` : hint;
706
1097
  }
707
1098
  async function promptForPackageManager(detectedPackageManager) {
708
1099
  const packageManager = await select({
@@ -744,7 +1135,9 @@ async function promptForPackageManager(detectedPackageManager) {
744
1135
  }
745
1136
  async function promptForDependencyInstall(packageManager) {
746
1137
  const shouldInstall = await confirm({
747
- message: `Install dependencies now with ${getInstallCommand(packageManager)}?`,
1138
+ message: `Install dependencies now with ${getInstallCommand(packageManager)}? You can run it later.`,
1139
+ active: "Install now",
1140
+ inactive: "Skip for now",
748
1141
  initialValue: true
749
1142
  });
750
1143
  if (isCancel(shouldInstall)) {
@@ -753,17 +1146,6 @@ async function promptForDependencyInstall(packageManager) {
753
1146
  }
754
1147
  return Boolean(shouldInstall);
755
1148
  }
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
1149
  function getCommandErrorMessage(error) {
768
1150
  if (error instanceof Error && "stderr" in error) {
769
1151
  const stderr = String(error.stderr ?? "").trim();
@@ -775,17 +1157,22 @@ async function collectPrismaSetupContext(input, options = {}) {
775
1157
  const projectDir = path.resolve(options.projectDir ?? process.cwd());
776
1158
  const useDefaults = input.yes === true;
777
1159
  const verbose = input.verbose === true;
778
- const shouldGenerate = input.generate ?? DEFAULT_GENERATE;
1160
+ const shouldEmit = input.emit ?? DEFAULT_EMIT;
779
1161
  const databaseProvider = input.provider ?? (useDefaults ? DEFAULT_DATABASE_PROVIDER : await promptForDatabaseProvider());
780
1162
  if (!databaseProvider) return;
781
- const schemaPreset = input.schemaPreset ?? options.defaultSchemaPreset ?? DEFAULT_SCHEMA_PRESET$1;
782
1163
  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;
1164
+ const shouldUsePrismaPostgres = input.prismaPostgres ?? (databaseProvider === "postgres" && !databaseUrl && !useDefaults ? await promptForPrismaPostgres() : DEFAULT_AUTOMATED_PRISMA_POSTGRES);
1165
+ if (shouldUsePrismaPostgres === void 0) return;
1166
+ if (shouldUsePrismaPostgres && databaseProvider !== "postgres") {
1167
+ cancel("--prisma-postgres is only supported with --provider postgres.");
1168
+ return;
788
1169
  }
1170
+ if (shouldUsePrismaPostgres && databaseUrl) {
1171
+ cancel("Use either --database-url or --prisma-postgres, not both.");
1172
+ return;
1173
+ }
1174
+ const authoring = input.authoring ?? (useDefaults ? DEFAULT_AUTHORING : await promptForAuthoringStyle());
1175
+ if (!authoring) return;
789
1176
  const detectedPackageManager = await detectPackageManager(projectDir);
790
1177
  const packageManager = input.packageManager ?? (useDefaults ? detectedPackageManager : await promptForPackageManager(detectedPackageManager));
791
1178
  if (!packageManager) return;
@@ -794,9 +1181,9 @@ async function collectPrismaSetupContext(input, options = {}) {
794
1181
  return {
795
1182
  projectDir,
796
1183
  verbose,
797
- shouldGenerate,
1184
+ shouldEmit,
798
1185
  databaseProvider,
799
- schemaPreset,
1186
+ authoring,
800
1187
  databaseUrl,
801
1188
  shouldUsePrismaPostgres,
802
1189
  packageManager,
@@ -805,14 +1192,11 @@ async function collectPrismaSetupContext(input, options = {}) {
805
1192
  }
806
1193
  function getDefaultDatabaseUrl(provider) {
807
1194
  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;";
1195
+ case "postgres": return "postgresql://user:password@localhost:5432/mydb";
1196
+ case "mongo": return "mongodb://localhost:27017/mydb?replicaSet=rs0&directConnection=true";
813
1197
  default: {
814
1198
  const exhaustiveCheck = provider;
815
- throw new Error(`Unsupported provider: ${String(exhaustiveCheck)}`);
1199
+ throw new Error(`Unsupported Prisma Next target: ${String(exhaustiveCheck)}`);
816
1200
  }
817
1201
  }
818
1202
  }
@@ -882,6 +1266,48 @@ async function ensureGitignoreEntry(projectDir, entry) {
882
1266
  const separator = existingContent.endsWith("\n") ? "" : "\n";
883
1267
  await fs.appendFile(gitignorePath, `${separator}${entry}\n`, "utf8");
884
1268
  }
1269
+ async function ensurePackageScripts(projectDir, scripts) {
1270
+ const packageJsonPath = path.join(projectDir, "package.json");
1271
+ if (!await fs.pathExists(packageJsonPath)) return;
1272
+ const packageJson = await fs.readJson(packageJsonPath);
1273
+ if (!packageJson.scripts) packageJson.scripts = {};
1274
+ let didChange = false;
1275
+ for (const [scriptName, command] of Object.entries(scripts)) if (typeof packageJson.scripts[scriptName] !== "string" || packageJson.scripts[scriptName].trim().length === 0) {
1276
+ packageJson.scripts[scriptName] = command;
1277
+ didChange = true;
1278
+ }
1279
+ if (didChange) await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
1280
+ }
1281
+ async function ensureMongoMemoryServerScript(projectDir) {
1282
+ const scriptPath = path.join(projectDir, "scripts", "mongo.mjs");
1283
+ if (await fs.pathExists(scriptPath)) return;
1284
+ await fs.ensureDir(path.dirname(scriptPath));
1285
+ await fs.writeFile(scriptPath, MONGO_MEMORY_SERVER_SCRIPT, "utf8");
1286
+ }
1287
+ async function ensureMongoMemoryServerDevDependency(projectDir) {
1288
+ const packageJsonPath = path.join(projectDir, "package.json");
1289
+ if (!await fs.pathExists(packageJsonPath)) return;
1290
+ const packageJson = await fs.readJson(packageJsonPath);
1291
+ if (!packageJson.devDependencies) packageJson.devDependencies = {};
1292
+ const memoryServerVersion = getDependencyVersion("mongodb-memory-server");
1293
+ if (packageJson.devDependencies["mongodb-memory-server"] === memoryServerVersion) return;
1294
+ packageJson.devDependencies["mongodb-memory-server"] = memoryServerVersion;
1295
+ packageJson.devDependencies = Object.fromEntries(Object.entries(packageJson.devDependencies).sort(([a], [b]) => a.localeCompare(b)));
1296
+ await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
1297
+ }
1298
+ async function writeMongoLocalHelpersForContext(context, projectDir) {
1299
+ if (context.databaseProvider !== "mongo" || context.databaseUrl) return true;
1300
+ try {
1301
+ await ensureMongoMemoryServerScript(projectDir);
1302
+ await ensureMongoMemoryServerDevDependency(projectDir);
1303
+ await ensurePackageScripts(projectDir, getMongoMemoryScripts(context.packageManager));
1304
+ await ensureGitignoreEntry(projectDir, ".mongo-data");
1305
+ return true;
1306
+ } catch (error) {
1307
+ cancel(getCommandErrorMessage(error));
1308
+ return false;
1309
+ }
1310
+ }
885
1311
  async function ensureRequiredPrismaFiles(projectDir) {
886
1312
  const missingFiles = [];
887
1313
  for (const candidates of requiredPrismaFileGroups) {
@@ -895,41 +1321,37 @@ async function ensureRequiredPrismaFiles(projectDir) {
895
1321
  }
896
1322
  if (!foundCandidate) missingFiles.push(candidates.join(" or "));
897
1323
  }
898
- if (missingFiles.length > 0) throw new Error(`Template is missing required Prisma files: ${missingFiles.join(", ")}`);
1324
+ if (missingFiles.length > 0) throw new Error(`Template is missing required Prisma Next files: ${missingFiles.join(", ")}`);
899
1325
  }
900
1326
  async function finalizePrismaFiles(options) {
901
1327
  const projectDir = options.projectDir ?? process.cwd();
902
- const prismaProjectDir = await resolvePrismaProjectDir(projectDir);
903
1328
  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), {
1329
+ await ensureEnvVarInEnv(projectDir, "DATABASE_URL", options.databaseUrl ?? getDefaultDatabaseUrl(options.provider), {
906
1330
  mode: options.databaseUrl ? "upsert" : "keep-existing",
907
1331
  comment: "Added by create-prisma"
908
1332
  });
909
1333
  if (options.claimUrl) {
910
- await ensureEnvVarInEnv(prismaProjectDir, "CLAIM_URL", options.claimUrl, {
1334
+ await ensureEnvVarInEnv(projectDir, "CLAIM_URL", options.claimUrl, {
911
1335
  mode: "upsert",
912
1336
  comment: PRISMA_POSTGRES_TEMPORARY_NOTICE
913
1337
  });
914
- await ensureEnvComment(prismaProjectDir, PRISMA_POSTGRES_TEMPORARY_NOTICE);
1338
+ await ensureEnvComment(projectDir, PRISMA_POSTGRES_TEMPORARY_NOTICE);
915
1339
  }
916
- await ensureGitignoreEntry(prismaProjectDir, generatedDir);
1340
+ await ensureGitignoreEntry(projectDir, ".env");
917
1341
  }
918
1342
  async function provisionPrismaPostgresIfNeeded(context, projectDir) {
919
1343
  if (!context.shouldUsePrismaPostgres) return { databaseUrl: context.databaseUrl };
920
1344
  const createDbCommand = getCreateDbCommand(context.packageManager);
921
- const prismaPostgresSpinner = spinner();
922
- prismaPostgresSpinner.start(`Provisioning Prisma Postgres with ${createDbCommand}...`);
1345
+ if (context.verbose) log.step(`Running ${createDbCommand}`);
923
1346
  try {
924
1347
  const prismaPostgresResult = await provisionPrismaPostgres(context.packageManager, projectDir);
925
- prismaPostgresSpinner.stop("Prisma Postgres database provisioned.");
1348
+ if (context.verbose) log.success("Prisma Postgres database provisioned.");
926
1349
  return {
927
1350
  databaseUrl: prismaPostgresResult.databaseUrl,
928
1351
  claimUrl: prismaPostgresResult.claimUrl
929
1352
  };
930
1353
  } catch (error) {
931
1354
  const errorMessage = error instanceof Error ? error.message : String(error);
932
- prismaPostgresSpinner.stop("Could not provision Prisma Postgres.");
933
1355
  return {
934
1356
  databaseUrl: context.databaseUrl,
935
1357
  warning: `Prisma Postgres provisioning failed: ${errorMessage}`
@@ -937,44 +1359,86 @@ async function provisionPrismaPostgresIfNeeded(context, projectDir) {
937
1359
  }
938
1360
  }
939
1361
  async function writeDependenciesForContext(context, projectDir) {
940
- const prismaProjectDir = await resolvePrismaProjectDir(projectDir);
941
1362
  try {
942
- await writePrismaDependencies(context.databaseProvider, context.packageManager, prismaProjectDir);
1363
+ await writePrismaDependencies(context.databaseProvider, context.packageManager, context.authoring, projectDir);
943
1364
  return true;
944
1365
  } catch (error) {
945
1366
  cancel(getCommandErrorMessage(error));
946
1367
  return false;
947
1368
  }
948
1369
  }
1370
+ function getPrismaNextCliPackageSpecifier() {
1371
+ return getPrismaNextPackageSpecifier("prisma-next");
1372
+ }
1373
+ function getPrismaNextInitTarget(provider) {
1374
+ return provider === "mongo" ? "mongodb" : "postgres";
1375
+ }
1376
+ function getPrismaNextInitCliArgs(packageManager, prismaNextArgs) {
1377
+ if (packageManager === "npm") return {
1378
+ command: "npx",
1379
+ args: [
1380
+ "--yes",
1381
+ getPrismaNextCliPackageSpecifier(),
1382
+ "init",
1383
+ ...prismaNextArgs
1384
+ ]
1385
+ };
1386
+ return getPackageExecutionArgs(packageManager, [
1387
+ getPrismaNextCliPackageSpecifier(),
1388
+ "init",
1389
+ ...prismaNextArgs
1390
+ ]);
1391
+ }
1392
+ function getPrismaNextInitCliCommand(packageManager, prismaNextArgs) {
1393
+ const execution = getPrismaNextInitCliArgs(packageManager, prismaNextArgs);
1394
+ return [execution.command, ...execution.args].join(" ");
1395
+ }
1396
+ async function runPrismaNextInitForContext(context, projectDir) {
1397
+ const initArgs = [
1398
+ "--yes",
1399
+ "--force",
1400
+ "--target",
1401
+ getPrismaNextInitTarget(context.databaseProvider),
1402
+ "--authoring",
1403
+ context.authoring,
1404
+ "--schema-path",
1405
+ getContractPath(context.authoring),
1406
+ "--no-install"
1407
+ ];
1408
+ const initCommand = getPrismaNextInitCliCommand(context.packageManager, initArgs);
1409
+ if (context.verbose) log.step(`Running ${initCommand}`);
1410
+ try {
1411
+ const initExecution = getPrismaNextInitCliArgs(context.packageManager, initArgs);
1412
+ await execa(initExecution.command, initExecution.args, {
1413
+ cwd: projectDir,
1414
+ stdio: context.verbose ? "inherit" : "pipe",
1415
+ env: {
1416
+ ...process.env,
1417
+ CI: "1"
1418
+ }
1419
+ });
1420
+ if (context.verbose) log.success("Prisma Next project files ready.");
1421
+ return true;
1422
+ } catch (error) {
1423
+ if (context.verbose) log.warn("Could not run Prisma Next init.");
1424
+ cancel(`Failed to run ${initCommand}: ${getCommandErrorMessage(error)}`);
1425
+ return false;
1426
+ }
1427
+ }
949
1428
  async function installDependenciesForContext(context, projectDir) {
950
1429
  if (!context.shouldInstall) return true;
951
1430
  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}...`);
1431
+ if (context.verbose) log.step(`Running ${installCommand}`);
965
1432
  try {
966
1433
  await installProjectDependencies(context.packageManager, projectDir, { verbose: context.verbose });
967
- installSpinner.stop("Dependencies installed.");
1434
+ if (context.verbose) log.success("Dependencies installed.");
968
1435
  return true;
969
1436
  } catch (error) {
970
- installSpinner.stop("Could not install dependencies.");
971
1437
  cancel(`Failed to run ${installCommand}: ${getCommandErrorMessage(error)}`);
972
1438
  return false;
973
1439
  }
974
1440
  }
975
1441
  async function finalizePrismaFilesForContext(context, projectDir, provisionResult) {
976
- const initSpinner = spinner();
977
- initSpinner.start("Preparing Prisma files...");
978
1442
  try {
979
1443
  await finalizePrismaFiles({
980
1444
  provider: context.databaseProvider,
@@ -982,746 +1446,165 @@ async function finalizePrismaFilesForContext(context, projectDir, provisionResul
982
1446
  claimUrl: provisionResult.claimUrl,
983
1447
  projectDir
984
1448
  });
985
- initSpinner.stop("Prisma files ready.");
1449
+ if (context.verbose) log.success("Prisma Next environment configured.");
986
1450
  return true;
987
1451
  } catch (error) {
988
- initSpinner.stop("Could not prepare Prisma files.");
989
1452
  cancel(getCommandErrorMessage(error));
990
1453
  return false;
991
1454
  }
992
1455
  }
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...");
1456
+ function getPrismaNextCliCommand(packageManager, prismaNextArgs) {
1457
+ if (packageManager === "deno") return `deno run -A --env-file=.env ${getDenoPrismaSpecifier()} ${prismaNextArgs.join(" ")}`;
1458
+ return getLocalPackageBinaryCommand(packageManager, "prisma-next", prismaNextArgs);
1459
+ }
1460
+ function getPrismaNextCliArgs(packageManager, prismaNextArgs) {
1461
+ if (packageManager === "deno") return {
1462
+ command: "deno",
1463
+ args: [
1464
+ "run",
1465
+ "-A",
1466
+ "--env-file=.env",
1467
+ getDenoPrismaSpecifier(),
1468
+ ...prismaNextArgs
1469
+ ]
1470
+ };
1471
+ return getLocalPackageBinaryArgs(packageManager, "prisma-next", prismaNextArgs);
1472
+ }
1473
+ async function emitPrismaNextContractForContext(context, projectDir) {
1474
+ if (!context.shouldEmit) return { didEmitContract: false };
1475
+ if (!context.shouldInstall) return {
1476
+ didEmitContract: false,
1477
+ warning: "Skipped contract emit because dependencies were not installed."
1478
+ };
1479
+ const emitCommand = getPrismaNextCliCommand(context.packageManager, ["contract", "emit"]);
1480
+ if (context.verbose) log.step(`Running ${emitCommand}`);
1000
1481
  try {
1001
- const generateArgs = getPrismaCliArgs(context.packageManager, ["generate"]);
1002
- await execa(generateArgs.command, generateArgs.args, {
1003
- cwd: prismaProjectDir,
1482
+ const emitArgs = getPrismaNextCliArgs(context.packageManager, ["contract", "emit"]);
1483
+ await execa(emitArgs.command, emitArgs.args, {
1484
+ cwd: projectDir,
1004
1485
  stdio: context.verbose ? "inherit" : "pipe"
1005
1486
  });
1006
- if (context.verbose) log.success("Prisma Client generated.");
1007
- else generateSpinner?.stop("Prisma Client generated.");
1008
- return { didGenerateClient: true };
1487
+ if (context.verbose) log.success("Prisma Next contract artifacts emitted.");
1488
+ return { didEmitContract: true };
1009
1489
  } catch (error) {
1010
- if (context.verbose) log.warn("Could not generate Prisma Client.");
1011
- else generateSpinner?.stop("Could not generate Prisma Client.");
1490
+ if (context.verbose) log.warn("Could not emit Prisma Next contract.");
1012
1491
  return {
1013
- didGenerateClient: false,
1014
- warning: `Prisma generate failed: ${getCommandErrorMessage(error)}`
1492
+ didEmitContract: false,
1493
+ warning: `Contract emit failed: ${getCommandErrorMessage(error)}`
1015
1494
  };
1016
1495
  }
1017
1496
  }
1018
- function buildWarningLines(provisionWarning, generateWarning) {
1497
+ function buildWarningLines(provisionWarning, emitWarning) {
1019
1498
  const warningLines = [];
1020
1499
  if (provisionWarning) warningLines.push(`- ${provisionWarning}`);
1021
- if (generateWarning) warningLines.push(`- ${generateWarning}`);
1500
+ if (emitWarning) warningLines.push(`- ${emitWarning}`);
1022
1501
  return warningLines;
1023
1502
  }
1024
1503
  function buildNextStepsForContext(opts) {
1025
- const { context, options, didGenerateClient } = opts;
1504
+ const { context, options, didEmitContract } = opts;
1026
1505
  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
1506
+ if (!context.shouldInstall) nextSteps.push({
1507
+ command: getInstallCommand(context.packageManager),
1508
+ description: "Install the project dependencies."
1047
1509
  });
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
1510
+ if (!didEmitContract || !context.shouldEmit) nextSteps.push({
1511
+ command: getRunScriptCommand(context.packageManager, "contract:emit"),
1512
+ description: "Emit contract.json and TypeScript types from your Prisma Next contract."
1325
1513
  });
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
- }]
1514
+ if (context.databaseProvider === "postgres") nextSteps.push({
1515
+ command: getRunScriptCommand(context.packageManager, "db:init"),
1516
+ description: "Create the initial PostgreSQL database objects and sign the database."
1345
1517
  });
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))
1518
+ if (context.databaseProvider === "mongo" && !context.databaseUrl) nextSteps.push({
1519
+ command: getRunScriptCommand(context.packageManager, "db:up"),
1520
+ description: "Start the local MongoDB replica set with mongodb-memory-server. Stop with `db:down`, wipe with `db:reset`."
1360
1521
  });
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]
1522
+ nextSteps.push({
1523
+ command: getRunScriptCommand(context.packageManager, "migration:plan"),
1524
+ description: "Compare the contract to the database and write a migration plan."
1373
1525
  });
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]
1526
+ nextSteps.push({
1527
+ command: getRunScriptCommand(context.packageManager, "migrate"),
1528
+ description: "Apply the planned migration to the database."
1386
1529
  });
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
1530
+ nextSteps.push({
1531
+ command: getRunScriptCommand(context.packageManager, "db:seed"),
1532
+ description: "Insert the sample users from prisma/seed.ts."
1399
1533
  });
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
- }
1534
+ if (options.includeDevNextStep) nextSteps.push({
1535
+ command: getRunScriptCommand(context.packageManager, "dev"),
1536
+ description: "Start the development server."
1441
1537
  });
1538
+ return nextSteps;
1442
1539
  }
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
- }
1540
+ function formatNextSteps(nextSteps) {
1541
+ return nextSteps.map((step) => `${step.command}\n ${step.description}`).join("\n\n");
1496
1542
  }
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
- }
1543
+ function formatAgentPrompt() {
1544
+ return [
1545
+ "Ask your agent:",
1546
+ "What can I do with Prisma Next?",
1547
+ "",
1548
+ "Learn more:",
1549
+ `Docs: prisma-next.md`,
1550
+ "Skills: https://github.com/prisma/prisma-next/tree/main/skills"
1551
+ ].join("\n");
1507
1552
  }
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
- }
1553
+ async function executePrismaSetupContext(context, options = {}) {
1554
+ const projectDir = path.resolve(options.projectDir ?? context.projectDir);
1555
+ const progressSpinner = context.verbose ? void 0 : options.progressSpinner ?? spinner();
1556
+ if (progressSpinner !== void 0 && !options.progressSpinner) progressSpinner.start("Creating Prisma Next project...");
1557
+ const stopProgressOnFailure = () => {
1558
+ progressSpinner?.stop("Could not create Prisma Next project.");
1559
+ };
1560
+ if (context.shouldUsePrismaPostgres) progressSpinner?.message("Provisioning Prisma Postgres...");
1561
+ const provisionResult = await provisionPrismaPostgresIfNeeded(context, projectDir);
1562
+ if (!provisionResult) {
1563
+ stopProgressOnFailure();
1564
+ return false;
1538
1565
  }
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);
1566
+ progressSpinner?.message("Preparing Prisma Next project files...");
1567
+ if (!await runPrismaNextInitForContext(context, projectDir)) {
1568
+ stopProgressOnFailure();
1569
+ return false;
1556
1570
  }
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);
1571
+ if (!await writeDependenciesForContext(context, projectDir)) {
1572
+ stopProgressOnFailure();
1573
+ return false;
1566
1574
  }
1567
- if (context.addons.includes("extension")) {
1568
- const extensionWarnings = await installExtensionAddon({
1569
- projectDir,
1570
- verbose,
1571
- targets: context.extensionTargets
1572
- });
1573
- warnings.push(...extensionWarnings);
1575
+ if (!await writeMongoLocalHelpersForContext(context, projectDir)) {
1576
+ stopProgressOnFailure();
1577
+ return false;
1574
1578
  }
1575
- if (warnings.length > 0) {
1576
- addonSpinner.stop("Add-ons applied with warnings.");
1577
- for (const warning of warnings) log.warn(warning);
1578
- return;
1579
+ if (context.shouldInstall) progressSpinner?.message("Installing dependencies...");
1580
+ if (!await installDependenciesForContext(context, projectDir)) {
1581
+ stopProgressOnFailure();
1582
+ return false;
1579
1583
  }
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(() => {});
1584
+ progressSpinner?.message("Configuring Prisma Next...");
1585
+ if (!await finalizePrismaFilesForContext(context, projectDir, provisionResult)) {
1586
+ stopProgressOnFailure();
1587
+ return false;
1654
1588
  }
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)
1589
+ if (context.shouldEmit && context.shouldInstall) progressSpinner?.message("Emitting Prisma Next contract artifacts...");
1590
+ const emitResult = await emitPrismaNextContractForContext(context, projectDir);
1591
+ const warningLines = buildWarningLines(provisionResult.warning, emitResult.warning);
1592
+ const nextSteps = buildNextStepsForContext({
1593
+ context,
1594
+ options,
1595
+ didEmitContract: emitResult.didEmitContract
1719
1596
  });
1597
+ progressSpinner?.stop("Prisma Next project ready.");
1598
+ if (warningLines.length > 0) note(warningLines.map((line) => line.replace(/^- /, "")).join("\n"), "Heads up");
1599
+ note(formatAgentPrompt(), "Agent prompt");
1600
+ if (context.verbose) note(formatNextSteps(nextSteps), "Next steps for Prisma Next");
1601
+ outro("Prisma Next setup complete.");
1602
+ return true;
1720
1603
  }
1721
1604
 
1722
1605
  //#endregion
1723
1606
  //#region src/ui/branding.ts
1724
- const prismaTitle = `${styleText(["bold", "cyan"], "Create")} ${styleText(["bold", "magenta"], "Prisma")}`;
1607
+ const prismaTitle = `${styleText(["bold", "cyanBright"], "◭")} ${styleText(["bold", "cyanBright"], "Create")} ${styleText(["bold", "magentaBright"], "Prisma")} ${styleText(["bold", "blueBright"], "Next")}`;
1725
1608
  function getCreatePrismaIntro() {
1726
1609
  return prismaTitle;
1727
1610
  }
@@ -1729,8 +1612,7 @@ function getCreatePrismaIntro() {
1729
1612
  //#endregion
1730
1613
  //#region src/commands/create.ts
1731
1614
  const DEFAULT_PROJECT_NAME = "my-app";
1732
- const DEFAULT_TEMPLATE = "hono";
1733
- const DEFAULT_SCHEMA_PRESET = "basic";
1615
+ const DEFAULT_TEMPLATE = "minimal";
1734
1616
  function toPackageName(projectName) {
1735
1617
  return projectName.toLowerCase().replace(/[^a-z0-9._-]/g, "-").replace(/^-+/, "").replace(/-+$/, "") || "app";
1736
1618
  }
@@ -1761,50 +1643,50 @@ async function promptForCreateTemplate() {
1761
1643
  message: "Select template",
1762
1644
  initialValue: DEFAULT_TEMPLATE,
1763
1645
  options: [
1646
+ {
1647
+ value: "minimal",
1648
+ label: "Minimal",
1649
+ hint: "Script-first Prisma Next starter with no web framework"
1650
+ },
1764
1651
  {
1765
1652
  value: "hono",
1766
1653
  label: "Hono",
1767
- hint: "TypeScript API starter"
1654
+ hint: "Lightweight TypeScript API server"
1768
1655
  },
1769
1656
  {
1770
1657
  value: "elysia",
1771
1658
  label: "Elysia",
1772
- hint: "TypeScript API starter with Elysia's Node adapter"
1659
+ hint: "Bun-friendly TypeScript API server"
1773
1660
  },
1774
1661
  {
1775
1662
  value: "nest",
1776
1663
  label: "NestJS",
1777
- hint: "Official Nest-style API starter with a Prisma service"
1664
+ hint: "Structured Node API with controllers and services"
1778
1665
  },
1779
1666
  {
1780
1667
  value: "next",
1781
1668
  label: "Next.js",
1782
- hint: "App Router + TypeScript starter"
1669
+ hint: "Full-stack React app with App Router"
1783
1670
  },
1784
1671
  {
1785
1672
  value: "svelte",
1786
1673
  label: "SvelteKit",
1787
- hint: "Official minimal SvelteKit + TypeScript starter"
1674
+ hint: "Full-stack Svelte 5 app with Vite"
1788
1675
  },
1789
1676
  {
1790
1677
  value: "astro",
1791
1678
  label: "Astro",
1792
- hint: "Official minimal Astro starter with API route example"
1679
+ hint: "Content-oriented web app with server routes"
1793
1680
  },
1794
1681
  {
1795
1682
  value: "nuxt",
1796
1683
  label: "Nuxt",
1797
- hint: "Official minimal Nuxt starter with Nitro API route example"
1684
+ hint: "Full-stack Vue app with Nitro server routes"
1798
1685
  },
1799
1686
  {
1800
1687
  value: "tanstack-start",
1801
1688
  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"
1689
+ hint: "React app with file routes and server functions"
1808
1690
  }
1809
1691
  ]
1810
1692
  });
@@ -1872,8 +1754,8 @@ async function runCreateCommand(rawInput = {}) {
1872
1754
  }
1873
1755
  }
1874
1756
  async function collectCreateContext(input) {
1875
- const useDefaults = input.yes === true;
1876
1757
  const force = input.force === true;
1758
+ const useDefaults = input.yes === true;
1877
1759
  const projectNameInput = input.name ?? (useDefaults ? DEFAULT_PROJECT_NAME : await promptForProjectName());
1878
1760
  if (projectNameInput === void 0) return;
1879
1761
  const projectName = String(projectNameInput).trim();
@@ -1894,42 +1776,33 @@ async function collectCreateContext(input) {
1894
1776
  cancel(`Target directory ${formatPathForDisplay(targetDirectory)} is not empty. Use --force to continue.`);
1895
1777
  return;
1896
1778
  }
1897
- const prismaSetupContext = await collectPrismaSetupContext(input, {
1898
- projectDir: targetDirectory,
1899
- defaultSchemaPreset: DEFAULT_SCHEMA_PRESET
1900
- });
1779
+ const prismaSetupContext = await collectPrismaSetupContext(input, { projectDir: targetDirectory });
1901
1780
  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
1781
  return {
1909
1782
  targetDirectory,
1910
1783
  targetPathState,
1911
1784
  force,
1912
1785
  template,
1913
1786
  projectPackageName: toPackageName(path.basename(targetDirectory)),
1914
- prismaSetupContext,
1915
- addonSetupContext: addonSetupContext ?? void 0
1787
+ prismaSetupContext
1916
1788
  };
1917
1789
  }
1918
1790
  async function executeCreateContext(context) {
1919
- const scaffoldSpinner = spinner();
1920
- scaffoldSpinner.start(`Scaffolding ${context.template} project...`);
1791
+ const createSpinner = context.prismaSetupContext.verbose ? void 0 : spinner();
1792
+ createSpinner?.start("Creating Prisma Next project...");
1921
1793
  try {
1794
+ if (context.prismaSetupContext.verbose) log.step(`Scaffolding ${context.template} starter.`);
1922
1795
  await scaffoldCreateTemplate({
1923
1796
  projectDir: context.targetDirectory,
1924
1797
  projectName: context.projectPackageName,
1925
1798
  template: context.template,
1926
- schemaPreset: context.prismaSetupContext.schemaPreset,
1927
1799
  provider: context.prismaSetupContext.databaseProvider,
1800
+ authoring: context.prismaSetupContext.authoring,
1928
1801
  packageManager: context.prismaSetupContext.packageManager
1929
1802
  });
1930
- scaffoldSpinner.stop("Project files scaffolded.");
1803
+ if (context.prismaSetupContext.verbose) log.success("Starter files scaffolded.");
1931
1804
  } catch (error) {
1932
- scaffoldSpinner.stop("Could not scaffold project files.");
1805
+ createSpinner?.stop("Could not create Prisma Next project.");
1933
1806
  return {
1934
1807
  ok: false,
1935
1808
  stage: "scaffold_template",
@@ -1943,6 +1816,7 @@ async function executeCreateContext(context) {
1943
1816
  projectDir: context.targetDirectory
1944
1817
  });
1945
1818
  } catch (error) {
1819
+ createSpinner?.stop("Could not create Prisma Next project.");
1946
1820
  return {
1947
1821
  ok: false,
1948
1822
  stage: "scaffold_template",
@@ -1950,31 +1824,22 @@ async function executeCreateContext(context) {
1950
1824
  };
1951
1825
  }
1952
1826
  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
- }
1827
+ const nextSteps = formatPathForDisplay(context.targetDirectory) === "." ? [] : [{
1828
+ command: `cd ${formatPathForDisplay(context.targetDirectory)}`,
1829
+ description: "Enter your new project directory."
1830
+ }];
1968
1831
  try {
1969
1832
  if (!await executePrismaSetupContext(context.prismaSetupContext, {
1970
1833
  prependNextSteps: nextSteps,
1971
1834
  projectDir: context.targetDirectory,
1972
- includeDevNextStep: true
1835
+ includeDevNextStep: true,
1836
+ progressSpinner: createSpinner
1973
1837
  })) return {
1974
1838
  ok: false,
1975
1839
  stage: "prisma_setup"
1976
1840
  };
1977
1841
  } catch (error) {
1842
+ createSpinner?.stop("Could not create Prisma Next project.");
1978
1843
  return {
1979
1844
  ok: false,
1980
1845
  stage: "prisma_setup",
@@ -1985,4 +1850,4 @@ async function executeCreateContext(context) {
1985
1850
  }
1986
1851
 
1987
1852
  //#endregion
1988
- export { DatabaseUrlSchema as a, DatabaseProviderSchema as i, CreateCommandInputSchema as n, PackageManagerSchema as o, CreateTemplateSchema as r, SchemaPresetSchema as s, runCreateCommand as t };
1853
+ export { DatabaseProviderSchema as a, CreateTemplateSchema as i, AuthoringStyleSchema as n, DatabaseUrlSchema as o, CreateCommandInputSchema as r, PackageManagerSchema as s, runCreateCommand as t };