@prisma/cli 3.0.0-dev.99.1 → 8.0.0-rc.2

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 (87) hide show
  1. package/README.md +3 -1
  2. package/dist/cli.js +15115 -16
  3. package/dist/deploy-framework-C7bQM00A.js +23 -0
  4. package/dist/payload-B88Qvzxk-CrtTXSOe.js +34 -0
  5. package/dist/sender.js +192 -0
  6. package/package.json +22 -18
  7. package/dist/adapters/git.js +0 -54
  8. package/dist/adapters/local-state.js +0 -144
  9. package/dist/adapters/mock-api.js +0 -136
  10. package/dist/adapters/token-storage.js +0 -418
  11. package/dist/cli2.js +0 -119
  12. package/dist/commands/app/index.js +0 -345
  13. package/dist/commands/auth/index.js +0 -96
  14. package/dist/commands/branch/index.js +0 -27
  15. package/dist/commands/database/index.js +0 -159
  16. package/dist/commands/env.js +0 -99
  17. package/dist/commands/git/index.js +0 -36
  18. package/dist/commands/project/index.js +0 -69
  19. package/dist/commands/version/index.js +0 -18
  20. package/dist/controllers/app-env-api.js +0 -54
  21. package/dist/controllers/app-env-file.js +0 -181
  22. package/dist/controllers/app-env.js +0 -502
  23. package/dist/controllers/app.js +0 -2443
  24. package/dist/controllers/auth.js +0 -316
  25. package/dist/controllers/branch.js +0 -103
  26. package/dist/controllers/database.js +0 -318
  27. package/dist/controllers/project.js +0 -731
  28. package/dist/controllers/select-prompt-port.js +0 -12
  29. package/dist/controllers/version.js +0 -12
  30. package/dist/lib/app/app-interaction.js +0 -5
  31. package/dist/lib/app/app-provider.js +0 -544
  32. package/dist/lib/app/branch-database-api.js +0 -102
  33. package/dist/lib/app/branch-database-deploy.js +0 -325
  34. package/dist/lib/app/branch-database.js +0 -215
  35. package/dist/lib/app/build-settings.js +0 -93
  36. package/dist/lib/app/build.js +0 -80
  37. package/dist/lib/app/bun-project.js +0 -45
  38. package/dist/lib/app/compute-config.js +0 -147
  39. package/dist/lib/app/deploy-output.js +0 -24
  40. package/dist/lib/app/deploy-plan.js +0 -58
  41. package/dist/lib/app/deploy-progress.js +0 -100
  42. package/dist/lib/app/domain-guidance.js +0 -14
  43. package/dist/lib/app/env-config.js +0 -67
  44. package/dist/lib/app/env-file.js +0 -82
  45. package/dist/lib/app/env-vars.js +0 -50
  46. package/dist/lib/app/local-dev.js +0 -109
  47. package/dist/lib/app/production-deploy-gate.js +0 -161
  48. package/dist/lib/app/read-branch.js +0 -30
  49. package/dist/lib/auth/auth-ops.js +0 -158
  50. package/dist/lib/auth/client.js +0 -22
  51. package/dist/lib/auth/guard.js +0 -38
  52. package/dist/lib/auth/login.js +0 -330
  53. package/dist/lib/database/provider.js +0 -167
  54. package/dist/lib/diagnostics.js +0 -15
  55. package/dist/lib/fs/home-path.js +0 -24
  56. package/dist/lib/git/local-branch.js +0 -53
  57. package/dist/lib/git/local-status.js +0 -57
  58. package/dist/lib/project/interactive-setup.js +0 -56
  59. package/dist/lib/project/local-pin.js +0 -200
  60. package/dist/lib/project/resolution.js +0 -386
  61. package/dist/lib/project/setup.js +0 -135
  62. package/dist/lib/version.js +0 -55
  63. package/dist/output/patterns.js +0 -90
  64. package/dist/presenters/app-env.js +0 -265
  65. package/dist/presenters/app.js +0 -646
  66. package/dist/presenters/auth.js +0 -183
  67. package/dist/presenters/branch.js +0 -46
  68. package/dist/presenters/database.js +0 -274
  69. package/dist/presenters/project.js +0 -174
  70. package/dist/presenters/verbose-context.js +0 -64
  71. package/dist/presenters/version.js +0 -29
  72. package/dist/shell/command-arguments.js +0 -6
  73. package/dist/shell/command-meta.js +0 -622
  74. package/dist/shell/command-runner.js +0 -112
  75. package/dist/shell/diagnostics-output.js +0 -57
  76. package/dist/shell/errors.js +0 -134
  77. package/dist/shell/global-flags.js +0 -37
  78. package/dist/shell/help.js +0 -89
  79. package/dist/shell/output.js +0 -82
  80. package/dist/shell/prompt.js +0 -41
  81. package/dist/shell/runtime.js +0 -55
  82. package/dist/shell/ui.js +0 -116
  83. package/dist/shell/update-check.js +0 -247
  84. package/dist/use-cases/auth.js +0 -145
  85. package/dist/use-cases/branch.js +0 -47
  86. package/dist/use-cases/create-cli-gateways.js +0 -68
  87. package/dist/use-cases/project.js +0 -30
@@ -1,325 +0,0 @@
1
- import { CliError, usageError } from "../../shell/errors.js";
2
- import { confirmPrompt } from "../../shell/prompt.js";
3
- import { renderSummaryLine } from "../../shell/ui.js";
4
- import { canPrompt } from "../../shell/runtime.js";
5
- import { formatCommandArgument } from "../../shell/command-arguments.js";
6
- import "../project/setup.js";
7
- import { hasBranchDatabaseSignal, inspectBranchDatabaseSignal } from "./branch-database.js";
8
- import path from "node:path";
9
- //#region src/lib/app/branch-database-deploy.ts
10
- async function maybeSetupBranchDatabase(context, provider, projectId, branch, options) {
11
- if (options.db === false) return emptyBranchDatabaseSetupOutcome();
12
- if (hasProvidedDatabaseEnvVars(options.providedEnvVars)) {
13
- if (options.db === true) throw usageError("Database setup cannot be combined with provided database env vars", "The deploy command received --db and a DATABASE_URL or DIRECT_URL value from --env.", "Remove the --env database value to let --db create and wire a database, or remove --db to deploy with the provided value.", ["prisma-cli app deploy --db", "prisma-cli app deploy --env DATABASE_URL=postgresql://example"], "app");
14
- return emptyBranchDatabaseSetupOutcome();
15
- }
16
- if (branch.kind === "production" && !options.firstProductionDeploy) {
17
- if (options.db === true) throw productionDatabaseSetupAfterFirstDeployError();
18
- return emptyBranchDatabaseSetupOutcome();
19
- }
20
- const envState = await inspectBranchDatabaseEnv(provider, projectId, branch, context.runtime.signal);
21
- const targetEnvVars = getTargetDatabaseEnvVarKeys(envState);
22
- if (hasExistingDatabaseEnvForTarget(branch, envState)) {
23
- const warning = options.db === true ? existingDatabaseEnvWarning(branch, targetEnvVars) : null;
24
- if (warning) emitBranchDatabaseWarning(context, warning);
25
- return {
26
- result: options.db === true ? {
27
- status: "skipped",
28
- reason: existingDatabaseEnvReason(branch),
29
- envVars: targetEnvVars
30
- } : void 0,
31
- warnings: warning ? [warning] : []
32
- };
33
- }
34
- const localSignal = await inspectBranchDatabaseSignal(options.projectDir, context.runtime.signal);
35
- if (localSignal.unsupportedSchema) {
36
- if (options.db === true) throw unsupportedBranchDatabaseSchemaError(localSignal.unsupportedSchema, branch, context);
37
- return emptyBranchDatabaseSetupOutcome();
38
- }
39
- const hasSignal = hasBranchDatabaseSignal(localSignal) || Boolean(envState.inheritedPreviewDatabaseUrl);
40
- if (options.db !== true) {
41
- if (!hasSignal) return emptyBranchDatabaseSetupOutcome();
42
- if (!canPrompt(context) || context.flags.yes) {
43
- const warning = databasePromptSuppressedWarning(branch);
44
- emitBranchDatabaseWarning(context, warning);
45
- return {
46
- result: void 0,
47
- warnings: [warning]
48
- };
49
- }
50
- maybeRenderBranchDatabaseSignal(context, branch, localSignal, envState);
51
- if (!await confirmPrompt({
52
- input: context.runtime.stdin,
53
- output: context.output.stderr,
54
- message: databasePromptMessage(branch),
55
- initialValue: false
56
- })) return emptyBranchDatabaseSetupOutcome();
57
- } else if (!canPrompt(context) && !context.flags.yes) throw nonInteractiveDatabaseSetupRequiresYesError(branch);
58
- return setupBranchDatabase(context, provider, projectId, branch, localSignal, envState, options.projectDir);
59
- }
60
- async function setupBranchDatabase(context, provider, projectId, branch, signal, envState, projectDir) {
61
- emitBranchDatabaseProgress(context, "pending", "Creating database");
62
- const database = await provider.createBranchDatabase({
63
- projectId,
64
- branchId: branch.id,
65
- branchName: branch.name,
66
- signal: context.runtime.signal
67
- }).catch((error) => {
68
- throw branchDatabaseSetupFailedError("Failed to create database", error, branch);
69
- });
70
- emitBranchDatabaseProgress(context, "success", "Created database");
71
- try {
72
- const envVars = await upsertBranchDatabaseEnvVars(context, provider, projectId, branch, database, envState);
73
- emitBranchDatabaseProgress(context, "success", `Added ${envScopeLabel(branch)} env var${envVars.length === 1 ? "" : "s"} ${envVars.join(", ")} — shared by every app on ${envScopeLabel(branch) === "production" ? "production" : `branch "${branch.name}"`}`);
74
- const schemaCommand = signal.schema ? `DATABASE_URL=<url> npx ${formatSchemaSetupCommand(signal.schema.command)} (detected ${path.relative(projectDir, signal.schema.path) || signal.schema.path})` : "your own migration tooling";
75
- const schemaSuggestion = `The new database is empty. Get a connection URL with \`prisma-cli database connection create ${database.id}\`, then apply your schema with ${schemaCommand}.`;
76
- emitBranchDatabaseWarning(context, schemaSuggestion);
77
- return {
78
- result: {
79
- status: "created",
80
- database: {
81
- id: database.id,
82
- name: database.name
83
- },
84
- envVars
85
- },
86
- warnings: [schemaSuggestion]
87
- };
88
- } catch (error) {
89
- throw await cleanupCreatedBranchDatabaseAfterFailure(context, provider, database, branch, error);
90
- }
91
- }
92
- async function upsertBranchDatabaseEnvVars(context, provider, projectId, branch, database, envState) {
93
- const scope = envScopeForBranch(branch);
94
- const written = [];
95
- await upsertBranchDatabaseEnvVar(context, provider, {
96
- projectId,
97
- ...scope,
98
- key: "DATABASE_URL",
99
- value: database.databaseUrl,
100
- existing: envState.targetDatabaseUrl,
101
- branch
102
- });
103
- written.push("DATABASE_URL");
104
- if (database.directUrl) {
105
- await upsertBranchDatabaseEnvVar(context, provider, {
106
- projectId,
107
- ...scope,
108
- key: "DIRECT_URL",
109
- value: database.directUrl,
110
- existing: envState.targetDirectUrl,
111
- branch
112
- });
113
- written.push("DIRECT_URL");
114
- } else if (branch.kind === "preview" && envState.targetDirectUrl) await provider.deleteEnvironmentVariable({
115
- envVarId: envState.targetDirectUrl.id,
116
- signal: context.runtime.signal
117
- }).catch((error) => {
118
- throw branchDatabaseSetupFailedError("Failed to remove stale DIRECT_URL", error, branch);
119
- });
120
- return written;
121
- }
122
- async function upsertBranchDatabaseEnvVar(context, provider, options) {
123
- if (options.existing) {
124
- await provider.updateEnvironmentVariable({
125
- envVarId: options.existing.id,
126
- value: options.value,
127
- signal: context.runtime.signal
128
- }).catch((error) => {
129
- throw branchDatabaseSetupFailedError(`Failed to update ${options.key}`, error, options.branch);
130
- });
131
- return;
132
- }
133
- await provider.createEnvironmentVariable({
134
- projectId: options.projectId,
135
- className: options.className,
136
- key: options.key,
137
- value: options.value,
138
- ...options.branchId ? { branchId: options.branchId } : {},
139
- signal: context.runtime.signal
140
- }).catch((error) => {
141
- throw branchDatabaseSetupFailedError(`Failed to write ${options.key}`, error, options.branch);
142
- });
143
- }
144
- async function inspectBranchDatabaseEnv(provider, projectId, branch, signal) {
145
- const scope = envScopeForBranch(branch);
146
- const [databaseUrlRows, directUrlRows] = await Promise.all([provider.listEnvironmentVariables({
147
- projectId,
148
- className: scope.className,
149
- key: "DATABASE_URL",
150
- signal
151
- }), provider.listEnvironmentVariables({
152
- projectId,
153
- className: scope.className,
154
- key: "DIRECT_URL",
155
- signal
156
- })]);
157
- const targetBranchId = branch.kind === "preview" ? branch.id : null;
158
- return {
159
- targetDatabaseUrl: findEnvVar(databaseUrlRows, { branchId: targetBranchId }),
160
- targetDirectUrl: findEnvVar(directUrlRows, { branchId: targetBranchId }),
161
- inheritedPreviewDatabaseUrl: branch.kind === "preview" ? findEnvVar(databaseUrlRows, { branchId: null }) : null
162
- };
163
- }
164
- function findEnvVar(rows, options) {
165
- return rows.find((row) => row.branchId === options.branchId) ?? null;
166
- }
167
- function hasProvidedDatabaseEnvVars(envVars) {
168
- return Boolean(envVars && ("DATABASE_URL" in envVars || "DIRECT_URL" in envVars));
169
- }
170
- function envScopeForBranch(branch) {
171
- return branch.kind === "production" ? { className: "production" } : {
172
- className: "preview",
173
- branchId: branch.id
174
- };
175
- }
176
- function envScopeLabel(branch) {
177
- return branch.kind === "production" ? "production" : "branch";
178
- }
179
- function getTargetDatabaseEnvVarKeys(envState) {
180
- return [envState.targetDatabaseUrl, envState.targetDirectUrl].filter((variable) => Boolean(variable)).map((variable) => variable.key).sort();
181
- }
182
- function hasExistingDatabaseEnvForTarget(branch, envState) {
183
- if (branch.kind === "production") return Boolean(envState.targetDatabaseUrl || envState.targetDirectUrl);
184
- return Boolean(envState.targetDatabaseUrl);
185
- }
186
- function existingDatabaseEnvReason(branch) {
187
- return branch.kind === "production" ? "production-env-exists" : "branch-env-exists";
188
- }
189
- function existingDatabaseEnvWarning(branch, envVars) {
190
- if (branch.kind === "production") return `Production already has ${envVars.join(" and ")}. Treating it as BYO database configuration and leaving env vars unchanged.`;
191
- return `Branch "${branch.name}" already has DATABASE_URL. Leaving branch database env vars unchanged.`;
192
- }
193
- function databasePromptSuppressedWarning(branch) {
194
- if (branch.kind === "production") return "This app appears to use DATABASE_URL. Run prisma-cli app deploy --db --yes to create and wire a Prisma Postgres database for this first production deploy.";
195
- return "This app appears to use DATABASE_URL. Run prisma-cli app deploy --db to create an isolated database for this preview branch.";
196
- }
197
- function databasePromptMessage(branch) {
198
- return branch.kind === "production" ? "Create a Prisma Postgres database for production?" : `Create an isolated database for branch "${branch.name}"?`;
199
- }
200
- function maybeRenderBranchDatabaseSignal(context, branch, signal, envState) {
201
- if (context.flags.json || context.flags.quiet) return;
202
- const rows = [
203
- signal.schema ? ` Schema ${path.relative(context.runtime.cwd, signal.schema.path) || defaultSchemaSourcePath(signal.schema)}` : null,
204
- signal.databaseUrlReferences.length > 0 ? ` Code ${signal.databaseUrlReferences.slice(0, 3).join(", ")}` : null,
205
- envState.inheritedPreviewDatabaseUrl ? " Env preview DATABASE_URL is inherited by this branch" : null
206
- ].filter((row) => Boolean(row));
207
- context.output.stderr.write(`Database signal found for ${databaseTargetLabel(branch)}\n${rows.join("\n")}\n\n`);
208
- }
209
- function databaseTargetLabel(branch) {
210
- return branch.kind === "production" ? `production branch "${branch.name}"` : `branch "${branch.name}"`;
211
- }
212
- function emitBranchDatabaseProgress(context, status, message) {
213
- if (context.flags.json || context.flags.quiet) return;
214
- const line = status === "pending" ? `${context.ui.warning("◇")} ${message}...` : renderSummaryLine(context.ui, "success", message);
215
- context.output.stderr.write(`${line}\n`);
216
- }
217
- function emitBranchDatabaseWarning(context, warning) {
218
- if (context.flags.json || context.flags.quiet) return;
219
- context.output.stderr.write(`${renderSummaryLine(context.ui, "warning", warning)}\n`);
220
- }
221
- function emptyBranchDatabaseSetupOutcome() {
222
- return {
223
- result: void 0,
224
- warnings: []
225
- };
226
- }
227
- function productionDatabaseSetupAfterFirstDeployError() {
228
- return usageError("Database setup is only available during the first production deploy", "The selected production app already has a live deployment.", "Use project env commands to manage production DATABASE_URL, or deploy a preview branch with --db.", ["prisma-cli project env add DATABASE_URL=<value> --role production", "prisma-cli app deploy --branch feature/db --db"], "app");
229
- }
230
- function nonInteractiveDatabaseSetupRequiresYesError(branch) {
231
- return usageError("Database setup requires --yes in non-interactive mode", "The deploy command received --db, but prompts are not available and --yes was not passed.", "Pass --yes together with --db to confirm non-interactive database creation.", [branch.kind === "production" ? "prisma-cli app deploy --prod --db --yes" : `prisma-cli app deploy --branch ${formatCommandArgument(branch.name)} --db --yes`], "app");
232
- }
233
- function formatSchemaSetupCommand(command) {
234
- switch (command) {
235
- case "migrate-deploy": return "prisma migrate deploy";
236
- case "db-push": return "prisma db push";
237
- case "prisma-next-db-init": return "prisma-next db init";
238
- }
239
- }
240
- function branchDatabaseSetupFailedError(summary, error, branch) {
241
- return new CliError({
242
- code: "BRANCH_DATABASE_SETUP_FAILED",
243
- domain: "app",
244
- summary,
245
- why: error instanceof Error ? error.message : String(error),
246
- fix: "Retry the command, or create the database and env vars manually with project env commands.",
247
- debug: formatDebugDetails(error),
248
- meta: { branch: branch.name },
249
- exitCode: 1,
250
- nextSteps: [formatAppDeployWithDbNextStep(branch), formatProjectEnvListNextStep(branch)]
251
- });
252
- }
253
- async function cleanupCreatedBranchDatabaseAfterFailure(context, provider, database, branch, error) {
254
- const setupError = error instanceof CliError ? error : branchDatabaseSetupFailedError("Database setup failed", error, branch);
255
- emitBranchDatabaseProgress(context, "pending", "Removing database after setup failed");
256
- try {
257
- await provider.deleteBranchDatabase({
258
- databaseId: database.id,
259
- signal: context.runtime.signal
260
- });
261
- emitBranchDatabaseProgress(context, "success", "Removed database after setup failed");
262
- } catch (cleanupError) {
263
- return branchDatabaseCleanupFailedError(setupError, cleanupError, database, branch);
264
- }
265
- return setupError;
266
- }
267
- function branchDatabaseCleanupFailedError(setupError, cleanupError, database, branch) {
268
- const cleanupWhy = cleanupError instanceof Error ? cleanupError.message : String(cleanupError);
269
- const setupWhy = setupError.why ?? "Database setup failed.";
270
- return new CliError({
271
- code: setupError.code,
272
- domain: setupError.domain,
273
- summary: setupError.summary,
274
- why: `${setupWhy} Prisma could not delete the created database "${database.name}" (${database.id}): ${cleanupWhy}`,
275
- fix: "Delete the created database from Console or contact Prisma support, then rerun deploy with --db.",
276
- debug: formatCombinedDebugDetails(setupError, cleanupError),
277
- meta: {
278
- ...setupError.meta,
279
- branch: branch.name,
280
- databaseId: database.id,
281
- databaseName: database.name,
282
- cleanupFailed: true
283
- },
284
- exitCode: setupError.exitCode,
285
- nextSteps: []
286
- });
287
- }
288
- function unsupportedBranchDatabaseSchemaError(schema, branch, context) {
289
- return usageError("Database setup is not available for this Prisma schema", `${path.relative(context.runtime.cwd, schema.path) || defaultUnsupportedSchemaSourcePath(schema)} targets ${formatUnsupportedSchemaTarget(schema.target)}, but --db creates Prisma Postgres databases.`, "Use project env commands to provide a database URL, or switch the Prisma schema source to PostgreSQL before using --db.", [formatProjectEnvAddNextStep(branch), `prisma-cli app deploy --branch ${formatCommandArgument(branch.name)}`], "app");
290
- }
291
- function formatAppDeployWithDbNextStep(branch) {
292
- return `prisma-cli app deploy --branch ${formatCommandArgument(branch.name)} --db`;
293
- }
294
- function formatProjectEnvListNextStep(branch) {
295
- return branch.kind === "production" ? "prisma-cli project env list --role production" : `prisma-cli project env list --branch ${formatCommandArgument(branch.name)}`;
296
- }
297
- function formatProjectEnvAddNextStep(branch) {
298
- return branch.kind === "production" ? "prisma-cli project env add DATABASE_URL=<value> --role production" : `prisma-cli project env add DATABASE_URL=<value> --branch ${formatCommandArgument(branch.name)}`;
299
- }
300
- function defaultSchemaSourcePath(schema) {
301
- return schema.kind === "prisma-next" ? "prisma-next.config.ts" : "schema.prisma";
302
- }
303
- function defaultUnsupportedSchemaSourcePath(schema) {
304
- return schema.kind === "prisma-next" ? "prisma-next.config.ts" : "schema.prisma";
305
- }
306
- function formatUnsupportedSchemaTarget(target) {
307
- switch (target) {
308
- case "cockroachdb": return "CockroachDB";
309
- case "mongodb": return "MongoDB";
310
- case "mysql": return "MySQL";
311
- case "sqlite": return "SQLite";
312
- case "sqlserver": return "SQL Server";
313
- }
314
- }
315
- function formatDebugDetails(error) {
316
- if (error instanceof Error) return error.stack ?? error.message;
317
- return typeof error === "string" ? error : null;
318
- }
319
- function formatCombinedDebugDetails(setupError, cleanupError) {
320
- const setupDebug = setupError.debug ?? setupError.stack ?? setupError.message;
321
- const cleanupDebug = formatDebugDetails(cleanupError);
322
- return [setupDebug ? `Setup error:\n${setupDebug}` : null, cleanupDebug ? `Cleanup error:\n${cleanupDebug}` : null].filter((line) => Boolean(line)).join("\n\n") || null;
323
- }
324
- //#endregion
325
- export { maybeSetupBranchDatabase };
@@ -1,215 +0,0 @@
1
- import { access, readFile, readdir, stat } from "node:fs/promises";
2
- import path from "node:path";
3
- //#region src/lib/app/branch-database.ts
4
- const SKIPPED_DIRECTORIES = new Set([
5
- ".git",
6
- ".next",
7
- ".nuxt",
8
- ".output",
9
- ".prisma",
10
- ".turbo",
11
- ".vercel",
12
- ".wrangler",
13
- "build",
14
- "coverage",
15
- "dist",
16
- "node_modules",
17
- "out"
18
- ]);
19
- const DATABASE_URL_SCAN_EXTENSIONS = new Set([
20
- ".cjs",
21
- ".cts",
22
- ".env",
23
- ".js",
24
- ".json",
25
- ".jsx",
26
- ".mjs",
27
- ".mts",
28
- ".prisma",
29
- ".ts",
30
- ".tsx"
31
- ]);
32
- const MAX_SCAN_DEPTH = 6;
33
- const MAX_SCAN_FILES = 1e3;
34
- const MAX_DATABASE_URL_REFERENCE_FILES = 10;
35
- const MAX_TEXT_FILE_BYTES = 1024 * 1024;
36
- async function inspectBranchDatabaseSignal(cwd, signal) {
37
- const state = {
38
- filesVisited: 0,
39
- schemaCandidates: [],
40
- prismaNextConfigCandidates: [],
41
- databaseUrlReferences: []
42
- };
43
- await scanDirectory(cwd, cwd, 0, state, signal);
44
- const prismaNextConfigs = await Promise.all(state.prismaNextConfigCandidates.map((configPath) => classifyPrismaNextConfig(configPath, signal)));
45
- const supportedPrismaNextConfig = selectPrismaNextConfig(cwd, prismaNextConfigs, "supported");
46
- const unsupportedPrismaNextConfig = selectPrismaNextConfig(cwd, prismaNextConfigs, "unsupported");
47
- const selectedPrismaOrmSchema = await selectPrismaOrmSchema(cwd, state.schemaCandidates, signal);
48
- const schema = supportedPrismaNextConfig ? {
49
- kind: "prisma-next",
50
- path: supportedPrismaNextConfig.path,
51
- hasMigrations: false,
52
- command: "prisma-next-db-init",
53
- target: supportedPrismaNextConfig.target
54
- } : selectedPrismaOrmSchema.schema;
55
- return {
56
- schema,
57
- unsupportedSchema: schema ? null : unsupportedPrismaNextConfig ? {
58
- kind: "prisma-next",
59
- path: unsupportedPrismaNextConfig.path,
60
- target: unsupportedPrismaNextConfig.target
61
- } : selectedPrismaOrmSchema.unsupportedSchema,
62
- databaseUrlReferences: state.databaseUrlReferences
63
- };
64
- }
65
- function hasBranchDatabaseSignal(signal) {
66
- if (signal.unsupportedSchema) return false;
67
- return Boolean(signal.schema || signal.databaseUrlReferences.length > 0);
68
- }
69
- async function scanDirectory(cwd, directory, depth, state, signal) {
70
- signal.throwIfAborted();
71
- if (depth > MAX_SCAN_DEPTH || state.filesVisited >= MAX_SCAN_FILES) return;
72
- let entries;
73
- try {
74
- entries = await readdir(directory, { withFileTypes: true });
75
- } catch (error) {
76
- if (error.code === "ENOENT") return;
77
- throw error;
78
- }
79
- entries.sort((left, right) => left.name.localeCompare(right.name));
80
- for (const entry of entries) {
81
- signal.throwIfAborted();
82
- if (state.filesVisited >= MAX_SCAN_FILES) return;
83
- const entryPath = path.join(directory, entry.name);
84
- if (entry.isDirectory()) {
85
- if (!SKIPPED_DIRECTORIES.has(entry.name)) await scanDirectory(cwd, entryPath, depth + 1, state, signal);
86
- continue;
87
- }
88
- if (!entry.isFile()) continue;
89
- state.filesVisited += 1;
90
- if (entry.name === "schema.prisma") state.schemaCandidates.push(entryPath);
91
- if (isPrismaNextConfigFile(entry.name)) state.prismaNextConfigCandidates.push(entryPath);
92
- if (state.databaseUrlReferences.length < MAX_DATABASE_URL_REFERENCE_FILES && shouldScanForDatabaseUrl(entry.name) && await fileContainsDatabaseUrl(entryPath, signal)) state.databaseUrlReferences.push(path.relative(cwd, entryPath) || entry.name);
93
- }
94
- }
95
- async function selectPrismaOrmSchema(cwd, candidates, signal) {
96
- const sorted = sortByPreferredRelativePath(cwd, candidates, "schema.prisma");
97
- for (const schemaPath of sorted) {
98
- const target = await classifyPrismaOrmSchemaTarget(schemaPath, signal);
99
- if (target === "postgresql" || target === "unknown") {
100
- const hasMigrations = await hasMigrationsDirectory(path.dirname(schemaPath), signal);
101
- return {
102
- schema: {
103
- kind: "prisma-orm",
104
- path: schemaPath,
105
- hasMigrations,
106
- command: hasMigrations ? "migrate-deploy" : "db-push",
107
- target
108
- },
109
- unsupportedSchema: null
110
- };
111
- }
112
- return {
113
- schema: null,
114
- unsupportedSchema: {
115
- kind: "prisma-orm",
116
- path: schemaPath,
117
- target
118
- }
119
- };
120
- }
121
- return {
122
- schema: null,
123
- unsupportedSchema: null
124
- };
125
- }
126
- function selectPrismaNextConfig(cwd, candidates, mode) {
127
- const matches = candidates.filter((candidate) => {
128
- const isSupported = candidate.target === "postgresql" || candidate.target === "unknown";
129
- return mode === "supported" ? isSupported : !isSupported;
130
- });
131
- return sortByPreferredRelativePath(cwd, matches.map((candidate) => candidate.path), "prisma-next.config.ts").map((candidatePath) => matches.find((candidate) => candidate.path === candidatePath)).find((candidate) => Boolean(candidate)) ?? null;
132
- }
133
- function sortByPreferredRelativePath(cwd, candidates, preferredRootFile) {
134
- return candidates.map((candidate) => ({
135
- absolute: candidate,
136
- relative: path.relative(cwd, candidate) || preferredRootFile
137
- })).sort((left, right) => {
138
- if (left.relative === preferredRootFile) return -1;
139
- if (right.relative === preferredRootFile) return 1;
140
- return left.relative.length - right.relative.length || left.relative.localeCompare(right.relative);
141
- }).map((candidate) => candidate.absolute);
142
- }
143
- async function hasMigrationsDirectory(schemaDirectory, signal) {
144
- signal.throwIfAborted();
145
- const migrationsPath = path.join(schemaDirectory, "migrations");
146
- try {
147
- await access(migrationsPath);
148
- return (await readdir(migrationsPath)).length > 0;
149
- } catch (error) {
150
- if (error.code === "ENOENT") return false;
151
- throw error;
152
- }
153
- }
154
- async function classifyPrismaNextConfig(configPath, signal) {
155
- const content = await readTextFileIfSmall(configPath, signal);
156
- if (!content) return {
157
- path: configPath,
158
- target: "unknown"
159
- };
160
- if (content.includes("@prisma-next/postgres/config")) return {
161
- path: configPath,
162
- target: "postgresql"
163
- };
164
- if (content.includes("@prisma-next/mongo/config")) return {
165
- path: configPath,
166
- target: "mongodb"
167
- };
168
- if (content.includes("@prisma-next/sqlite/config")) return {
169
- path: configPath,
170
- target: "sqlite"
171
- };
172
- return {
173
- path: configPath,
174
- target: "unknown"
175
- };
176
- }
177
- async function classifyPrismaOrmSchemaTarget(schemaPath, signal) {
178
- switch ((await readTextFileIfSmall(schemaPath, signal))?.match(/\bprovider\s*=\s*"([^"]+)"/)?.[1] ?? null) {
179
- case "postgresql": return "postgresql";
180
- case "mongodb": return "mongodb";
181
- case "mysql": return "mysql";
182
- case "sqlite": return "sqlite";
183
- case "sqlserver": return "sqlserver";
184
- case "cockroachdb": return "cockroachdb";
185
- default: return "unknown";
186
- }
187
- }
188
- function shouldScanForDatabaseUrl(fileName) {
189
- if (fileName === ".env" || fileName.startsWith(".env.")) return true;
190
- return DATABASE_URL_SCAN_EXTENSIONS.has(path.extname(fileName));
191
- }
192
- function isPrismaNextConfigFile(fileName) {
193
- if (!fileName.startsWith("prisma-next.config.")) return false;
194
- return [
195
- ".cjs",
196
- ".cts",
197
- ".js",
198
- ".mjs",
199
- ".mts",
200
- ".ts"
201
- ].some((extension) => fileName.endsWith(extension));
202
- }
203
- async function fileContainsDatabaseUrl(filePath, signal) {
204
- return (await readTextFileIfSmall(filePath, signal))?.includes("DATABASE_URL") ?? false;
205
- }
206
- async function readTextFileIfSmall(filePath, signal) {
207
- signal.throwIfAborted();
208
- if ((await stat(filePath)).size > MAX_TEXT_FILE_BYTES) return null;
209
- return readFile(filePath, {
210
- encoding: "utf8",
211
- signal
212
- });
213
- }
214
- //#endregion
215
- export { hasBranchDatabaseSignal, inspectBranchDatabaseSignal };
@@ -1,93 +0,0 @@
1
- import { readFile } from "node:fs/promises";
2
- import path from "node:path";
3
- import { resolveBuildSettings, resolveConfiguredBuildSettings } from "@prisma/compute-sdk";
4
- //#region src/lib/app/build-settings.ts
5
- /** Legacy build-settings file: no longer read or written, only detected for migration. */
6
- const PRISMA_APP_CONFIG_FILENAME = "prisma.app.json";
7
- /**
8
- * Detects a leftover `prisma.app.json`. The file is no longer used: one that
9
- * matches the effective settings is reported for deletion, one with custom
10
- * values must be migrated to the compute config so builds never silently
11
- * change.
12
- */
13
- async function detectLegacyBuildSettings(options) {
14
- const configPath = path.join(options.appPath, PRISMA_APP_CONFIG_FILENAME);
15
- let content;
16
- try {
17
- options.signal?.throwIfAborted();
18
- content = await readFile(configPath, {
19
- encoding: "utf8",
20
- signal: options.signal
21
- });
22
- } catch (error) {
23
- if (options.signal?.aborted) throw error;
24
- return { kind: "absent" };
25
- }
26
- let legacy;
27
- try {
28
- const parsed = JSON.parse(content);
29
- const buildCommand = parsed.buildCommand === null || typeof parsed.buildCommand === "string" ? typeof parsed.buildCommand === "string" ? parsed.buildCommand.trim() || null : null : void 0;
30
- const outputDirectory = typeof parsed.outputDirectory === "string" ? normalizeRelativePath(parsed.outputDirectory) : void 0;
31
- if (buildCommand === void 0 || !outputDirectory) return {
32
- kind: "invalid",
33
- configPath
34
- };
35
- legacy = {
36
- buildCommand,
37
- outputDirectory
38
- };
39
- } catch {
40
- return {
41
- kind: "invalid",
42
- configPath
43
- };
44
- }
45
- return legacy.buildCommand === options.effective.buildCommand && legacy.outputDirectory === options.effective.outputDirectory ? {
46
- kind: "matching",
47
- configPath
48
- } : {
49
- kind: "custom",
50
- configPath,
51
- ...legacy
52
- };
53
- }
54
- /** Resolves build settings purely from framework inference; nothing is read or written. */
55
- async function resolveInferredAppBuildSettings(options) {
56
- return {
57
- status: "inferred",
58
- configPath: null,
59
- relativeConfigPath: null,
60
- settings: await resolveBuildSettings(options)
61
- };
62
- }
63
- /**
64
- * Resolves build settings when the compute config owns them: configured
65
- * fields win, omitted fields fall back to framework defaults.
66
- */
67
- async function resolveConfiguredAppBuildSettings(options) {
68
- const configFilename = path.basename(options.configPath);
69
- const settings = await resolveConfiguredBuildSettings({
70
- appPath: options.appPath,
71
- buildType: options.buildType,
72
- configured: options.configured,
73
- source: `set by ${configFilename}`,
74
- signal: options.signal
75
- });
76
- return {
77
- status: "config",
78
- configPath: options.configPath,
79
- relativeConfigPath: configFilename,
80
- settings
81
- };
82
- }
83
- function normalizeRelativePath(value) {
84
- const raw = value.trim().replace(/\\/g, "/");
85
- if (raw.length === 0 || raw.split("/").includes("..")) return;
86
- if (/^[A-Za-z]:/.test(raw)) return;
87
- const normalized = path.posix.normalize(raw);
88
- const segments = normalized.split("/");
89
- if (path.win32.isAbsolute(value) || path.posix.isAbsolute(normalized) || segments.includes("..")) return;
90
- return normalized === "." ? "." : normalized;
91
- }
92
- //#endregion
93
- export { PRISMA_APP_CONFIG_FILENAME, detectLegacyBuildSettings, resolveConfiguredAppBuildSettings, resolveInferredAppBuildSettings };