@prisma/cli 8.0.0-rc.1 → 8.0.0-rc.10

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 (110) hide show
  1. package/README.md +25 -29
  2. package/dist/cli.js +14551 -16
  3. package/dist/{v8/sender.js → sender.js} +1 -1
  4. package/package.json +17 -18
  5. package/dist/adapters/git.js +0 -54
  6. package/dist/adapters/local-state.js +0 -155
  7. package/dist/auth/client.js +0 -30
  8. package/dist/auth/errors.js +0 -50
  9. package/dist/auth/guard.js +0 -39
  10. package/dist/auth/login.js +0 -471
  11. package/dist/auth/operations.js +0 -208
  12. package/dist/auth/recipient.js +0 -42
  13. package/dist/auth/token-storage.js +0 -439
  14. package/dist/auth/workspaces.js +0 -171
  15. package/dist/cli-command.js +0 -14
  16. package/dist/cli-name.js +0 -12
  17. package/dist/cli2.js +0 -136
  18. package/dist/command-arguments.js +0 -12
  19. package/dist/commands/agent/index.js +0 -60
  20. package/dist/commands/app/index.js +0 -350
  21. package/dist/commands/auth/index.js +0 -95
  22. package/dist/commands/branch/index.js +0 -27
  23. package/dist/commands/bucket/index.js +0 -123
  24. package/dist/commands/build/index.js +0 -29
  25. package/dist/commands/database/index.js +0 -249
  26. package/dist/commands/env.js +0 -99
  27. package/dist/commands/feedback/index.js +0 -20
  28. package/dist/commands/git/index.js +0 -36
  29. package/dist/commands/init/index.js +0 -33
  30. package/dist/commands/project/index.js +0 -118
  31. package/dist/commands/version/index.js +0 -18
  32. package/dist/controllers/agent-setup.js +0 -52
  33. package/dist/controllers/agent.js +0 -228
  34. package/dist/controllers/app-env-api.js +0 -55
  35. package/dist/controllers/app-env-file.js +0 -181
  36. package/dist/controllers/app-env.js +0 -502
  37. package/dist/controllers/app.js +0 -2508
  38. package/dist/controllers/auth.js +0 -124
  39. package/dist/controllers/branch.js +0 -92
  40. package/dist/controllers/bucket.js +0 -184
  41. package/dist/controllers/build.js +0 -88
  42. package/dist/controllers/database.js +0 -463
  43. package/dist/controllers/feedback.js +0 -90
  44. package/dist/controllers/init.js +0 -814
  45. package/dist/controllers/project.js +0 -892
  46. package/dist/controllers/select-prompt-port.js +0 -13
  47. package/dist/controllers/version.js +0 -12
  48. package/dist/errors.js +0 -102
  49. package/dist/lib/agent/cli-command.js +0 -20
  50. package/dist/lib/agent/constants.js +0 -12
  51. package/dist/lib/agent/package-manager.js +0 -99
  52. package/dist/lib/agent/setup-status.js +0 -83
  53. package/dist/lib/app/app-provider.js +0 -617
  54. package/dist/lib/app/branch-database-api.js +0 -102
  55. package/dist/lib/app/branch-database-deploy.js +0 -353
  56. package/dist/lib/app/branch-database.js +0 -234
  57. package/dist/lib/app/build-settings.js +0 -98
  58. package/dist/lib/app/build.js +0 -82
  59. package/dist/lib/app/bun-project.js +0 -45
  60. package/dist/lib/app/compute-config.js +0 -145
  61. package/dist/lib/app/deploy-output.js +0 -24
  62. package/dist/lib/app/deploy-plan.js +0 -59
  63. package/dist/lib/app/deploy-progress.js +0 -100
  64. package/dist/lib/app/domain-guidance.js +0 -14
  65. package/dist/lib/app/env-config.js +0 -67
  66. package/dist/lib/app/env-file.js +0 -82
  67. package/dist/lib/app/env-vars.js +0 -50
  68. package/dist/lib/app/local-dev.js +0 -109
  69. package/dist/lib/app/production-deploy-gate.js +0 -162
  70. package/dist/lib/app/read-branch.js +0 -30
  71. package/dist/lib/bucket/provider.js +0 -139
  72. package/dist/lib/database/provider.js +0 -385
  73. package/dist/lib/diagnostics.js +0 -16
  74. package/dist/lib/feedback.js +0 -15
  75. package/dist/lib/fs/home-path.js +0 -24
  76. package/dist/lib/git/local-branch.js +0 -53
  77. package/dist/lib/git/local-status.js +0 -57
  78. package/dist/lib/project/interactive-setup.js +0 -57
  79. package/dist/lib/project/local-pin.js +0 -200
  80. package/dist/lib/project/provider.js +0 -93
  81. package/dist/lib/project/resolution.js +0 -389
  82. package/dist/lib/project/setup.js +0 -138
  83. package/dist/lib/version.js +0 -56
  84. package/dist/lib/workspace-id.js +0 -18
  85. package/dist/output/patterns.js +0 -90
  86. package/dist/presenters/agent.js +0 -74
  87. package/dist/presenters/app-env.js +0 -265
  88. package/dist/presenters/app.js +0 -672
  89. package/dist/presenters/auth.js +0 -184
  90. package/dist/presenters/branch.js +0 -46
  91. package/dist/presenters/bucket.js +0 -174
  92. package/dist/presenters/database.js +0 -462
  93. package/dist/presenters/feedback.js +0 -26
  94. package/dist/presenters/init.js +0 -30
  95. package/dist/presenters/project.js +0 -268
  96. package/dist/presenters/verbose-context.js +0 -64
  97. package/dist/presenters/version.js +0 -29
  98. package/dist/shell/cli-command.js +0 -2
  99. package/dist/shell/command-meta.js +0 -923
  100. package/dist/shell/command-runner.js +0 -119
  101. package/dist/shell/diagnostics-output.js +0 -57
  102. package/dist/shell/global-flags.js +0 -37
  103. package/dist/shell/help.js +0 -90
  104. package/dist/shell/output.js +0 -144
  105. package/dist/shell/prompt.js +0 -48
  106. package/dist/shell/runtime.js +0 -38
  107. package/dist/shell/ui.js +0 -116
  108. package/dist/state-dir.js +0 -12
  109. package/dist/update-check.js +0 -248
  110. package/dist/v8/cli.js +0 -14699
@@ -1,102 +0,0 @@
1
- //#region src/lib/app/branch-database-api.ts
2
- async function createBranchDatabase(client, options) {
3
- const result = await client.POST("/v1/databases", {
4
- body: {
5
- projectId: options.projectId,
6
- branchId: options.branchId,
7
- name: options.branchName,
8
- source: { type: "empty" }
9
- },
10
- signal: options.signal
11
- });
12
- if (result.error || !result.data) throw apiCallError(`Failed to create database for branch "${options.branchName}"`, result.response, result.error);
13
- return normalizeBranchDatabaseRecord(result.data.data);
14
- }
15
- async function listEnvironmentVariables(client, options) {
16
- const variables = [];
17
- let cursor;
18
- while (true) {
19
- const result = await client.GET("/v1/environment-variables", {
20
- params: { query: {
21
- projectId: options.projectId,
22
- class: options.className,
23
- key: options.key,
24
- branchId: options.branchId,
25
- cursor
26
- } },
27
- signal: options.signal
28
- });
29
- if (result.error || !result.data) throw apiCallError("Failed to list environment variables", result.response, result.error);
30
- variables.push(...result.data.data);
31
- if (!result.data.pagination.hasMore || !result.data.pagination.nextCursor) break;
32
- cursor = result.data.pagination.nextCursor;
33
- }
34
- return variables.map((variable) => normalizeEnvironmentVariable(variable));
35
- }
36
- async function createEnvironmentVariable(client, options) {
37
- const result = await client.POST("/v1/environment-variables", {
38
- body: {
39
- projectId: options.projectId,
40
- class: options.className,
41
- key: options.key,
42
- value: options.value,
43
- ...options.branchId ? { branchId: options.branchId } : {}
44
- },
45
- signal: options.signal
46
- });
47
- if (result.error || !result.data) throw apiCallError(`Failed to add ${options.key}`, result.response, result.error);
48
- return normalizeEnvironmentVariable(result.data.data);
49
- }
50
- async function deleteBranchDatabase(client, options) {
51
- const result = await client.DELETE("/v1/databases/{databaseId}", {
52
- params: { path: { databaseId: options.databaseId } },
53
- signal: options.signal
54
- });
55
- if (result.error) throw apiCallError("Failed to delete branch database", result.response, result.error);
56
- }
57
- async function updateEnvironmentVariable(client, options) {
58
- const result = await client.PATCH("/v1/environment-variables/{envVarId}", {
59
- params: { path: { envVarId: options.envVarId } },
60
- body: { value: options.value },
61
- signal: options.signal
62
- });
63
- if (result.error || !result.data) throw apiCallError("Failed to update environment variable", result.response, result.error);
64
- return normalizeEnvironmentVariable(result.data.data);
65
- }
66
- async function deleteEnvironmentVariable(client, options) {
67
- const result = await client.DELETE("/v1/environment-variables/{envVarId}", {
68
- params: { path: { envVarId: options.envVarId } },
69
- signal: options.signal
70
- });
71
- if (result.error) throw apiCallError("Failed to delete environment variable", result.response, result.error);
72
- }
73
- function normalizeEnvironmentVariable(variable) {
74
- return {
75
- id: variable.id,
76
- key: variable.key,
77
- branchId: variable.branchId,
78
- className: variable.class,
79
- isManagedBySystem: variable.isManagedBySystem
80
- };
81
- }
82
- function normalizeBranchDatabaseRecord(database) {
83
- const connection = database.connections?.[0];
84
- const databaseUrl = connection?.endpoints?.pooled?.connectionString;
85
- const directUrl = connection?.endpoints?.direct?.connectionString ?? null;
86
- if (!databaseUrl) throw new Error("Created database did not return a pooled connection string.");
87
- return {
88
- id: database.id,
89
- name: database.name,
90
- branchId: database.branchId,
91
- databaseUrl,
92
- directUrl
93
- };
94
- }
95
- function apiCallError(summary, response, error) {
96
- if (response.status === 404) return /* @__PURE__ */ new Error("Resource Not Found");
97
- const message = error.error?.message ?? `Management API returned HTTP ${response.status}.`;
98
- const hint = error.error?.hint ? ` ${error.error.hint}` : "";
99
- return /* @__PURE__ */ new Error(`${summary}: ${message}${hint}`);
100
- }
101
- //#endregion
102
- export { createBranchDatabase, createEnvironmentVariable, deleteBranchDatabase, deleteEnvironmentVariable, listEnvironmentVariables, updateEnvironmentVariable };
@@ -1,353 +0,0 @@
1
- import { formatCommandArgument } from "../../command-arguments.js";
2
- import { CliError, usageError } from "../../errors.js";
3
- import { renderSummaryLine } from "../../shell/ui.js";
4
- import { canPrompt } from "../../shell/runtime.js";
5
- import { confirmPrompt } from "../../shell/prompt.js";
6
- import { hasBranchDatabaseSignal, inspectBranchDatabaseSignal } from "./branch-database.js";
7
- import path from "node:path";
8
- //#region src/lib/app/branch-database-deploy.ts
9
- async function maybeSetupBranchDatabase(context, provider, projectId, branch, options) {
10
- if (options.db === false) return emptyBranchDatabaseSetupOutcome();
11
- const databaseRequested = options.db === true;
12
- const conflict = findBranchDatabaseSetupConflict(branch, options);
13
- if (conflict) {
14
- if (databaseRequested) throw branchDatabaseSetupConflictError(conflict);
15
- return emptyBranchDatabaseSetupOutcome();
16
- }
17
- const envState = await inspectBranchDatabaseEnv(provider, projectId, branch, context.runtime.signal);
18
- if (hasExistingDatabaseEnvForTarget(branch, envState)) return existingDatabaseEnvOutcome(context, branch, envState, databaseRequested);
19
- const localSignal = await inspectBranchDatabaseSignal(options.projectDir, context.runtime.signal);
20
- if (localSignal.unsupportedSchema) {
21
- if (databaseRequested) throw unsupportedBranchDatabaseSchemaError(localSignal.unsupportedSchema, branch, context);
22
- return emptyBranchDatabaseSetupOutcome();
23
- }
24
- const confirmation = await confirmBranchDatabaseSetup(context, branch, localSignal, envState, databaseRequested);
25
- if (!confirmation.confirmed) return confirmation.outcome;
26
- return setupBranchDatabase(context, provider, projectId, branch, localSignal, envState, options.projectDir);
27
- }
28
- function findBranchDatabaseSetupConflict(branch, options) {
29
- if (hasProvidedDatabaseEnvVars(options.providedEnvVars)) return "provided-env-vars";
30
- if (branch.kind === "production" && !options.firstProductionDeploy) return "production-after-first-deploy";
31
- return null;
32
- }
33
- function branchDatabaseSetupConflictError(conflict) {
34
- switch (conflict) {
35
- case "provided-env-vars": return providedDatabaseEnvVarsError();
36
- case "production-after-first-deploy": return productionDatabaseSetupAfterFirstDeployError();
37
- }
38
- }
39
- function existingDatabaseEnvOutcome(context, branch, envState, databaseRequested) {
40
- if (!databaseRequested) return emptyBranchDatabaseSetupOutcome();
41
- const envVars = getTargetDatabaseEnvVarKeys(envState);
42
- const warning = existingDatabaseEnvWarning(branch, envVars);
43
- emitBranchDatabaseWarning(context, warning);
44
- return {
45
- result: {
46
- status: "skipped",
47
- reason: existingDatabaseEnvReason(branch),
48
- envVars
49
- },
50
- warnings: [warning]
51
- };
52
- }
53
- async function confirmBranchDatabaseSetup(context, branch, signal, envState, databaseRequested) {
54
- if (databaseRequested) {
55
- if (!canPrompt(context) && !context.flags.yes) throw nonInteractiveDatabaseSetupRequiresYesError(branch);
56
- return { confirmed: true };
57
- }
58
- if (!(hasBranchDatabaseSignal(signal) || Boolean(envState.inheritedPreviewDatabaseUrl))) return {
59
- confirmed: false,
60
- outcome: emptyBranchDatabaseSetupOutcome()
61
- };
62
- if (!canPrompt(context) || context.flags.yes) {
63
- const warning = databasePromptSuppressedWarning(branch);
64
- emitBranchDatabaseWarning(context, warning);
65
- return {
66
- confirmed: false,
67
- outcome: {
68
- result: void 0,
69
- warnings: [warning]
70
- }
71
- };
72
- }
73
- maybeRenderBranchDatabaseSignal(context, branch, signal, envState);
74
- return await confirmPrompt({
75
- input: context.runtime.stdin,
76
- output: context.output.stderr,
77
- signal: context.runtime.signal,
78
- message: databasePromptMessage(branch),
79
- initialValue: false
80
- }) ? { confirmed: true } : {
81
- confirmed: false,
82
- outcome: emptyBranchDatabaseSetupOutcome()
83
- };
84
- }
85
- async function setupBranchDatabase(context, provider, projectId, branch, signal, envState, projectDir) {
86
- emitBranchDatabaseProgress(context, "pending", "Creating database");
87
- const database = await provider.createBranchDatabase({
88
- projectId,
89
- branchId: branch.id,
90
- branchName: branch.name,
91
- signal: context.runtime.signal
92
- }).catch((error) => {
93
- throw branchDatabaseSetupFailedError("Failed to create database", error, branch);
94
- });
95
- emitBranchDatabaseProgress(context, "success", "Created database");
96
- try {
97
- const envVars = await upsertBranchDatabaseEnvVars(context, provider, projectId, branch, database, envState);
98
- 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}"`}`);
99
- 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";
100
- 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}.`;
101
- emitBranchDatabaseWarning(context, schemaSuggestion);
102
- return {
103
- result: {
104
- status: "created",
105
- database: {
106
- id: database.id,
107
- name: database.name
108
- },
109
- envVars
110
- },
111
- warnings: [schemaSuggestion]
112
- };
113
- } catch (error) {
114
- throw await cleanupCreatedBranchDatabaseAfterFailure(context, provider, database, branch, error);
115
- }
116
- }
117
- async function upsertBranchDatabaseEnvVars(context, provider, projectId, branch, database, envState) {
118
- const scope = envScopeForBranch(branch);
119
- const written = [];
120
- await upsertBranchDatabaseEnvVar(context, provider, {
121
- projectId,
122
- ...scope,
123
- key: "DATABASE_URL",
124
- value: database.databaseUrl,
125
- existing: envState.targetDatabaseUrl,
126
- branch
127
- });
128
- written.push("DATABASE_URL");
129
- if (database.directUrl) {
130
- await upsertBranchDatabaseEnvVar(context, provider, {
131
- projectId,
132
- ...scope,
133
- key: "DIRECT_URL",
134
- value: database.directUrl,
135
- existing: envState.targetDirectUrl,
136
- branch
137
- });
138
- written.push("DIRECT_URL");
139
- } else if (branch.kind === "preview" && envState.targetDirectUrl) await provider.deleteEnvironmentVariable({
140
- envVarId: envState.targetDirectUrl.id,
141
- signal: context.runtime.signal
142
- }).catch((error) => {
143
- throw branchDatabaseSetupFailedError("Failed to remove stale DIRECT_URL", error, branch);
144
- });
145
- return written;
146
- }
147
- async function upsertBranchDatabaseEnvVar(context, provider, options) {
148
- if (options.existing) {
149
- await provider.updateEnvironmentVariable({
150
- envVarId: options.existing.id,
151
- value: options.value,
152
- signal: context.runtime.signal
153
- }).catch((error) => {
154
- throw branchDatabaseSetupFailedError(`Failed to update ${options.key}`, error, options.branch);
155
- });
156
- return;
157
- }
158
- await provider.createEnvironmentVariable({
159
- projectId: options.projectId,
160
- className: options.className,
161
- key: options.key,
162
- value: options.value,
163
- ...options.branchId ? { branchId: options.branchId } : {},
164
- signal: context.runtime.signal
165
- }).catch((error) => {
166
- throw branchDatabaseSetupFailedError(`Failed to write ${options.key}`, error, options.branch);
167
- });
168
- }
169
- async function inspectBranchDatabaseEnv(provider, projectId, branch, signal) {
170
- const scope = envScopeForBranch(branch);
171
- const [databaseUrlRows, directUrlRows] = await Promise.all([provider.listEnvironmentVariables({
172
- projectId,
173
- className: scope.className,
174
- key: "DATABASE_URL",
175
- signal
176
- }), provider.listEnvironmentVariables({
177
- projectId,
178
- className: scope.className,
179
- key: "DIRECT_URL",
180
- signal
181
- })]);
182
- const targetBranchId = branch.kind === "preview" ? branch.id : null;
183
- return {
184
- targetDatabaseUrl: findEnvVar(databaseUrlRows, { branchId: targetBranchId }),
185
- targetDirectUrl: findEnvVar(directUrlRows, { branchId: targetBranchId }),
186
- inheritedPreviewDatabaseUrl: branch.kind === "preview" ? findEnvVar(databaseUrlRows, { branchId: null }) : null
187
- };
188
- }
189
- function findEnvVar(rows, options) {
190
- return rows.find((row) => row.branchId === options.branchId) ?? null;
191
- }
192
- function hasProvidedDatabaseEnvVars(envVars) {
193
- return Boolean(envVars && ("DATABASE_URL" in envVars || "DIRECT_URL" in envVars));
194
- }
195
- function envScopeForBranch(branch) {
196
- return branch.kind === "production" ? { className: "production" } : {
197
- className: "preview",
198
- branchId: branch.id
199
- };
200
- }
201
- function envScopeLabel(branch) {
202
- return branch.kind === "production" ? "production" : "branch";
203
- }
204
- function getTargetDatabaseEnvVarKeys(envState) {
205
- return [envState.targetDatabaseUrl, envState.targetDirectUrl].filter((variable) => Boolean(variable)).map((variable) => variable.key).sort();
206
- }
207
- function hasExistingDatabaseEnvForTarget(branch, envState) {
208
- if (branch.kind === "production") return Boolean(envState.targetDatabaseUrl || envState.targetDirectUrl);
209
- return Boolean(envState.targetDatabaseUrl);
210
- }
211
- function existingDatabaseEnvReason(branch) {
212
- return branch.kind === "production" ? "production-env-exists" : "branch-env-exists";
213
- }
214
- function existingDatabaseEnvWarning(branch, envVars) {
215
- if (branch.kind === "production") return `Production already has ${envVars.join(" and ")}. Treating it as BYO database configuration and leaving env vars unchanged.`;
216
- return `Branch "${branch.name}" already has DATABASE_URL. Leaving branch database env vars unchanged.`;
217
- }
218
- function databasePromptSuppressedWarning(branch) {
219
- 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.";
220
- return "This app appears to use DATABASE_URL. Run prisma-cli app deploy --db to create an isolated database for this preview branch.";
221
- }
222
- function databasePromptMessage(branch) {
223
- return branch.kind === "production" ? "Create a Prisma Postgres database for production?" : `Create an isolated database for branch "${branch.name}"?`;
224
- }
225
- function maybeRenderBranchDatabaseSignal(context, branch, signal, envState) {
226
- if (context.flags.json || context.flags.quiet) return;
227
- const rows = [
228
- signal.schema ? ` Schema ${path.relative(context.runtime.cwd, signal.schema.path) || defaultSchemaSourcePath(signal.schema)}` : null,
229
- signal.databaseUrlReferences.length > 0 ? ` Code ${signal.databaseUrlReferences.slice(0, 3).join(", ")}` : null,
230
- envState.inheritedPreviewDatabaseUrl ? " Env preview DATABASE_URL is inherited by this branch" : null
231
- ].filter((row) => Boolean(row));
232
- context.output.stderr.write(`Database signal found for ${databaseTargetLabel(branch)}\n${rows.join("\n")}\n\n`);
233
- }
234
- function databaseTargetLabel(branch) {
235
- return branch.kind === "production" ? `production branch "${branch.name}"` : `branch "${branch.name}"`;
236
- }
237
- function emitBranchDatabaseProgress(context, status, message) {
238
- if (context.flags.json || context.flags.quiet) return;
239
- const line = status === "pending" ? `${context.ui.warning("◇")} ${message}...` : renderSummaryLine(context.ui, "success", message);
240
- context.output.stderr.write(`${line}\n`);
241
- }
242
- function emitBranchDatabaseWarning(context, warning) {
243
- if (context.flags.json || context.flags.quiet) return;
244
- context.output.stderr.write(`${renderSummaryLine(context.ui, "warning", warning)}\n`);
245
- }
246
- function emptyBranchDatabaseSetupOutcome() {
247
- return {
248
- result: void 0,
249
- warnings: []
250
- };
251
- }
252
- function providedDatabaseEnvVarsError() {
253
- return 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");
254
- }
255
- function productionDatabaseSetupAfterFirstDeployError() {
256
- 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");
257
- }
258
- function nonInteractiveDatabaseSetupRequiresYesError(branch) {
259
- 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");
260
- }
261
- function formatSchemaSetupCommand(command) {
262
- switch (command) {
263
- case "migrate-deploy": return "prisma migrate deploy";
264
- case "db-push": return "prisma db push";
265
- case "prisma-next-db-init": return "prisma-next db init";
266
- }
267
- }
268
- function branchDatabaseSetupFailedError(summary, error, branch) {
269
- return new CliError({
270
- code: "BRANCH_DATABASE_SETUP_FAILED",
271
- domain: "app",
272
- summary,
273
- why: error instanceof Error ? error.message : String(error),
274
- fix: "Retry the command, or create the database and env vars manually with project env commands.",
275
- debug: formatDebugDetails(error),
276
- meta: { branch: branch.name },
277
- exitCode: 1,
278
- nextSteps: [formatAppDeployWithDbNextStep(branch), formatProjectEnvListNextStep(branch)]
279
- });
280
- }
281
- async function cleanupCreatedBranchDatabaseAfterFailure(context, provider, database, branch, error) {
282
- const setupError = error instanceof CliError ? error : branchDatabaseSetupFailedError("Database setup failed", error, branch);
283
- emitBranchDatabaseProgress(context, "pending", "Removing database after setup failed");
284
- try {
285
- await provider.deleteBranchDatabase({
286
- databaseId: database.id,
287
- signal: context.runtime.signal
288
- });
289
- emitBranchDatabaseProgress(context, "success", "Removed database after setup failed");
290
- } catch (cleanupError) {
291
- return branchDatabaseCleanupFailedError(setupError, cleanupError, database, branch);
292
- }
293
- return setupError;
294
- }
295
- function branchDatabaseCleanupFailedError(setupError, cleanupError, database, branch) {
296
- const cleanupWhy = cleanupError instanceof Error ? cleanupError.message : String(cleanupError);
297
- const setupWhy = setupError.why ?? "Database setup failed.";
298
- return new CliError({
299
- code: setupError.code,
300
- domain: setupError.domain,
301
- summary: setupError.summary,
302
- why: `${setupWhy} Prisma could not delete the created database "${database.name}" (${database.id}): ${cleanupWhy}`,
303
- fix: "Delete the created database from Console or contact Prisma support, then rerun deploy with --db.",
304
- debug: formatCombinedDebugDetails(setupError, cleanupError),
305
- meta: {
306
- ...setupError.meta,
307
- branch: branch.name,
308
- databaseId: database.id,
309
- databaseName: database.name,
310
- cleanupFailed: true
311
- },
312
- exitCode: setupError.exitCode,
313
- nextSteps: []
314
- });
315
- }
316
- function unsupportedBranchDatabaseSchemaError(schema, branch, context) {
317
- 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");
318
- }
319
- function formatAppDeployWithDbNextStep(branch) {
320
- return `prisma-cli app deploy --branch ${formatCommandArgument(branch.name)} --db`;
321
- }
322
- function formatProjectEnvListNextStep(branch) {
323
- return branch.kind === "production" ? "prisma-cli project env list --role production" : `prisma-cli project env list --branch ${formatCommandArgument(branch.name)}`;
324
- }
325
- function formatProjectEnvAddNextStep(branch) {
326
- 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)}`;
327
- }
328
- function defaultSchemaSourcePath(schema) {
329
- return schema.kind === "prisma-next" ? "prisma-next.config.ts" : "schema.prisma";
330
- }
331
- function defaultUnsupportedSchemaSourcePath(schema) {
332
- return schema.kind === "prisma-next" ? "prisma-next.config.ts" : "schema.prisma";
333
- }
334
- function formatUnsupportedSchemaTarget(target) {
335
- switch (target) {
336
- case "cockroachdb": return "CockroachDB";
337
- case "mongodb": return "MongoDB";
338
- case "mysql": return "MySQL";
339
- case "sqlite": return "SQLite";
340
- case "sqlserver": return "SQL Server";
341
- }
342
- }
343
- function formatDebugDetails(error) {
344
- if (error instanceof Error) return error.stack ?? error.message;
345
- return typeof error === "string" ? error : null;
346
- }
347
- function formatCombinedDebugDetails(setupError, cleanupError) {
348
- const setupDebug = setupError.debug ?? setupError.stack ?? setupError.message;
349
- const cleanupDebug = formatDebugDetails(cleanupError);
350
- return [setupDebug ? `Setup error:\n${setupDebug}` : null, cleanupDebug ? `Cleanup error:\n${cleanupDebug}` : null].filter((line) => Boolean(line)).join("\n\n") || null;
351
- }
352
- //#endregion
353
- export { maybeSetupBranchDatabase };
@@ -1,234 +0,0 @@
1
- import path from "node:path";
2
- import { access, readFile, readdir, stat } from "node:fs/promises";
3
- //#region src/lib/app/branch-database.ts
4
- const SKIPPED_DIRECTORIES = new Set([
5
- ".git",
6
- ".agents",
7
- ".next",
8
- ".nuxt",
9
- ".output",
10
- ".prisma",
11
- ".turbo",
12
- ".vercel",
13
- ".wrangler",
14
- "build",
15
- "coverage",
16
- "dist",
17
- "node_modules",
18
- "out"
19
- ]);
20
- const DATABASE_URL_SCAN_EXTENSIONS = new Set([
21
- ".cjs",
22
- ".cts",
23
- ".env",
24
- ".js",
25
- ".json",
26
- ".jsx",
27
- ".mjs",
28
- ".mts",
29
- ".prisma",
30
- ".ts",
31
- ".tsx"
32
- ]);
33
- const MAX_SCAN_DEPTH = 6;
34
- const MAX_SCAN_FILES = 1e3;
35
- const MAX_DATABASE_URL_REFERENCE_FILES = 10;
36
- const MAX_TEXT_FILE_BYTES = 1024 * 1024;
37
- async function inspectBranchDatabaseSignal(cwd, signal) {
38
- const state = {
39
- filesVisited: 0,
40
- schemaCandidates: [],
41
- prismaNextConfigCandidates: [],
42
- databaseUrlReferences: []
43
- };
44
- await scanDirectory(cwd, cwd, 0, state, signal);
45
- const prismaNextConfigs = await Promise.all(state.prismaNextConfigCandidates.map((configPath) => classifyPrismaNextConfig(configPath, signal)));
46
- const supportedPrismaNextConfig = selectPrismaNextConfig(cwd, prismaNextConfigs, "supported");
47
- const unsupportedPrismaNextConfig = selectPrismaNextConfig(cwd, prismaNextConfigs, "unsupported");
48
- const selectedPrismaOrmSchema = await selectPrismaOrmSchema(cwd, state.schemaCandidates, signal);
49
- const schema = supportedPrismaNextConfig ? {
50
- kind: "prisma-next",
51
- path: supportedPrismaNextConfig.path,
52
- hasMigrations: false,
53
- command: "prisma-next-db-init",
54
- target: supportedPrismaNextConfig.target
55
- } : selectedPrismaOrmSchema.schema;
56
- return {
57
- schema,
58
- unsupportedSchema: schema ? null : toUnsupportedSchema(unsupportedPrismaNextConfig) ?? selectedPrismaOrmSchema.unsupportedSchema,
59
- databaseUrlReferences: state.databaseUrlReferences
60
- };
61
- }
62
- function toUnsupportedSchema(config) {
63
- if (!config) return null;
64
- return {
65
- kind: "prisma-next",
66
- path: config.path,
67
- target: config.target
68
- };
69
- }
70
- function hasBranchDatabaseSignal(signal) {
71
- if (signal.unsupportedSchema) return false;
72
- return Boolean(signal.schema || signal.databaseUrlReferences.length > 0);
73
- }
74
- async function scanDirectory(cwd, directory, depth, state, signal) {
75
- signal.throwIfAborted();
76
- if (depth > MAX_SCAN_DEPTH || state.filesVisited >= MAX_SCAN_FILES) return;
77
- const entries = await readSortedDirectoryEntries(directory);
78
- for (const entry of entries) {
79
- signal.throwIfAborted();
80
- if (state.filesVisited >= MAX_SCAN_FILES) return;
81
- const entryPath = path.join(directory, entry.name);
82
- if (isScannableDirectory(entry)) {
83
- await scanDirectory(cwd, entryPath, depth + 1, state, signal);
84
- continue;
85
- }
86
- if (!entry.isFile()) continue;
87
- state.filesVisited += 1;
88
- recordSchemaCandidates(entry.name, entryPath, state);
89
- 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);
90
- }
91
- }
92
- async function readSortedDirectoryEntries(directory) {
93
- let entries;
94
- try {
95
- entries = await readdir(directory, { withFileTypes: true });
96
- } catch (error) {
97
- if (error.code === "ENOENT") return [];
98
- throw error;
99
- }
100
- return entries.sort((left, right) => compareCodeUnits(left.name, right.name));
101
- }
102
- function compareCodeUnits(left, right) {
103
- if (left < right) return -1;
104
- return left > right ? 1 : 0;
105
- }
106
- function isScannableDirectory(entry) {
107
- return entry.isDirectory() && !SKIPPED_DIRECTORIES.has(entry.name);
108
- }
109
- function recordSchemaCandidates(fileName, filePath, state) {
110
- if (fileName === "schema.prisma") state.schemaCandidates.push(filePath);
111
- if (isPrismaNextConfigFile(fileName)) state.prismaNextConfigCandidates.push(filePath);
112
- }
113
- async function selectPrismaOrmSchema(cwd, candidates, signal) {
114
- const sorted = sortByPreferredRelativePath(cwd, candidates, "schema.prisma");
115
- for (const schemaPath of sorted) {
116
- const target = await classifyPrismaOrmSchemaTarget(schemaPath, signal);
117
- if (target === "postgresql" || target === "unknown") {
118
- const hasMigrations = await hasMigrationsDirectory(path.dirname(schemaPath), signal);
119
- return {
120
- schema: {
121
- kind: "prisma-orm",
122
- path: schemaPath,
123
- hasMigrations,
124
- command: hasMigrations ? "migrate-deploy" : "db-push",
125
- target
126
- },
127
- unsupportedSchema: null
128
- };
129
- }
130
- return {
131
- schema: null,
132
- unsupportedSchema: {
133
- kind: "prisma-orm",
134
- path: schemaPath,
135
- target
136
- }
137
- };
138
- }
139
- return {
140
- schema: null,
141
- unsupportedSchema: null
142
- };
143
- }
144
- function selectPrismaNextConfig(cwd, candidates, mode) {
145
- const matches = candidates.filter((candidate) => {
146
- const isSupported = candidate.target === "postgresql" || candidate.target === "unknown";
147
- return mode === "supported" ? isSupported : !isSupported;
148
- });
149
- 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;
150
- }
151
- function sortByPreferredRelativePath(cwd, candidates, preferredRootFile) {
152
- return candidates.map((candidate) => ({
153
- absolute: candidate,
154
- relative: path.relative(cwd, candidate) || preferredRootFile
155
- })).sort((left, right) => {
156
- if (left.relative === preferredRootFile) return -1;
157
- if (right.relative === preferredRootFile) return 1;
158
- return left.relative.length - right.relative.length || compareCodeUnits(left.relative, right.relative);
159
- }).map((candidate) => candidate.absolute);
160
- }
161
- async function hasMigrationsDirectory(schemaDirectory, signal) {
162
- signal.throwIfAborted();
163
- const migrationsPath = path.join(schemaDirectory, "migrations");
164
- try {
165
- await access(migrationsPath);
166
- return (await readdir(migrationsPath)).length > 0;
167
- } catch (error) {
168
- if (error.code === "ENOENT") return false;
169
- throw error;
170
- }
171
- }
172
- async function classifyPrismaNextConfig(configPath, signal) {
173
- const content = await readTextFileIfSmall(configPath, signal);
174
- if (!content) return {
175
- path: configPath,
176
- target: "unknown"
177
- };
178
- if (content.includes("@prisma-next/postgres/config")) return {
179
- path: configPath,
180
- target: "postgresql"
181
- };
182
- if (content.includes("@prisma-next/mongo/config")) return {
183
- path: configPath,
184
- target: "mongodb"
185
- };
186
- if (content.includes("@prisma-next/sqlite/config")) return {
187
- path: configPath,
188
- target: "sqlite"
189
- };
190
- return {
191
- path: configPath,
192
- target: "unknown"
193
- };
194
- }
195
- const SCHEMA_PROVIDER = /\bprovider\s*=\s*"([^"]+)"/;
196
- async function classifyPrismaOrmSchemaTarget(schemaPath, signal) {
197
- switch ((await readTextFileIfSmall(schemaPath, signal))?.match(SCHEMA_PROVIDER)?.[1] ?? null) {
198
- case "postgresql": return "postgresql";
199
- case "mongodb": return "mongodb";
200
- case "mysql": return "mysql";
201
- case "sqlite": return "sqlite";
202
- case "sqlserver": return "sqlserver";
203
- case "cockroachdb": return "cockroachdb";
204
- default: return "unknown";
205
- }
206
- }
207
- function shouldScanForDatabaseUrl(fileName) {
208
- if (fileName === ".env" || fileName.startsWith(".env.")) return true;
209
- return DATABASE_URL_SCAN_EXTENSIONS.has(path.extname(fileName));
210
- }
211
- function isPrismaNextConfigFile(fileName) {
212
- if (!fileName.startsWith("prisma-next.config.")) return false;
213
- return [
214
- ".cjs",
215
- ".cts",
216
- ".js",
217
- ".mjs",
218
- ".mts",
219
- ".ts"
220
- ].some((extension) => fileName.endsWith(extension));
221
- }
222
- async function fileContainsDatabaseUrl(filePath, signal) {
223
- return (await readTextFileIfSmall(filePath, signal))?.includes("DATABASE_URL") ?? false;
224
- }
225
- async function readTextFileIfSmall(filePath, signal) {
226
- signal.throwIfAborted();
227
- if ((await stat(filePath)).size > MAX_TEXT_FILE_BYTES) return null;
228
- return readFile(filePath, {
229
- encoding: "utf8",
230
- signal
231
- });
232
- }
233
- //#endregion
234
- export { hasBranchDatabaseSignal, inspectBranchDatabaseSignal };