@prisma/cli 3.0.0-dev.60.1 → 3.0.0-dev.61.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.
|
@@ -32,6 +32,10 @@ async function maybeSetupBranchDatabase(context, provider, projectId, branch, op
|
|
|
32
32
|
warnings: warning ? [warning] : []
|
|
33
33
|
};
|
|
34
34
|
}
|
|
35
|
+
if (localSignal.unsupportedSchema) {
|
|
36
|
+
if (options.db === true) throw unsupportedBranchDatabaseSchemaError(localSignal.unsupportedSchema, branch.name, context);
|
|
37
|
+
return emptyBranchDatabaseSetupOutcome();
|
|
38
|
+
}
|
|
35
39
|
const hasSignal = hasBranchDatabaseSignal(localSignal) || Boolean(envState.previewDatabaseUrl);
|
|
36
40
|
if (options.db !== true) {
|
|
37
41
|
if (!hasSignal) return emptyBranchDatabaseSetupOutcome();
|
|
@@ -76,10 +80,10 @@ async function setupBranchDatabase(context, provider, projectId, branch, signal,
|
|
|
76
80
|
databaseUrl: database.databaseUrl,
|
|
77
81
|
directUrl: database.directUrl
|
|
78
82
|
}).catch((error) => {
|
|
79
|
-
throw schemaSetupFailedError(error, signal.schema, branch.name);
|
|
83
|
+
throw schemaSetupFailedError(error, signal.schema, branch.name, context.runtime.cwd);
|
|
80
84
|
});
|
|
81
85
|
emitBranchDatabaseProgress(context, "success", "Applied database schema");
|
|
82
|
-
} else skippedSchemaWarning = "No schema
|
|
86
|
+
} else skippedSchemaWarning = "No supported Prisma schema source was found. Branch database env vars were created, but schema setup was skipped.";
|
|
83
87
|
const envVars = await upsertBranchDatabaseEnvVars(context, provider, projectId, branch, database, envState);
|
|
84
88
|
emitBranchDatabaseProgress(context, "success", `Added branch env override${envVars.length === 1 ? "" : "s"} ${envVars.join(", ")}`);
|
|
85
89
|
if (skippedSchemaWarning) {
|
|
@@ -96,6 +100,7 @@ async function setupBranchDatabase(context, provider, projectId, branch, signal,
|
|
|
96
100
|
envVars,
|
|
97
101
|
schema: schemaSetup ? {
|
|
98
102
|
command: schemaSetup.command,
|
|
103
|
+
source: schemaSetup.source,
|
|
99
104
|
path: schemaSetup.schemaPath
|
|
100
105
|
} : null
|
|
101
106
|
},
|
|
@@ -185,7 +190,7 @@ function hasInlineDatabaseEnvVars(envVars) {
|
|
|
185
190
|
function maybeRenderBranchDatabaseSignal(context, branchName, signal, envState) {
|
|
186
191
|
if (context.flags.json || context.flags.quiet) return;
|
|
187
192
|
const rows = [
|
|
188
|
-
signal.schema ? ` Schema ${path.relative(context.runtime.cwd, signal.schema.path) ||
|
|
193
|
+
signal.schema ? ` Schema ${path.relative(context.runtime.cwd, signal.schema.path) || defaultSchemaSourcePath(signal.schema)}` : null,
|
|
189
194
|
signal.databaseUrlReferences.length > 0 ? ` Code ${signal.databaseUrlReferences.slice(0, 3).join(", ")}` : null,
|
|
190
195
|
envState.previewDatabaseUrl ? " Env preview DATABASE_URL is inherited by this branch" : null
|
|
191
196
|
].filter((row) => Boolean(row));
|
|
@@ -207,7 +212,11 @@ function emptyBranchDatabaseSetupOutcome() {
|
|
|
207
212
|
};
|
|
208
213
|
}
|
|
209
214
|
function formatSchemaSetupCommand(command) {
|
|
210
|
-
|
|
215
|
+
switch (command) {
|
|
216
|
+
case "migrate-deploy": return "prisma migrate deploy";
|
|
217
|
+
case "db-push": return "prisma db push";
|
|
218
|
+
case "prisma-next-db-init": return "prisma-next db init";
|
|
219
|
+
}
|
|
211
220
|
}
|
|
212
221
|
function branchDatabaseSetupFailedError(summary, error, branchName) {
|
|
213
222
|
return new CliError({
|
|
@@ -257,7 +266,7 @@ function branchDatabaseCleanupFailedError(setupError, cleanupError, database, br
|
|
|
257
266
|
nextSteps: []
|
|
258
267
|
});
|
|
259
268
|
}
|
|
260
|
-
function schemaSetupFailedError(error, schema, branchName) {
|
|
269
|
+
function schemaSetupFailedError(error, schema, branchName, cwd) {
|
|
261
270
|
return new CliError({
|
|
262
271
|
code: "SCHEMA_SETUP_FAILED",
|
|
263
272
|
domain: "app",
|
|
@@ -268,12 +277,39 @@ function schemaSetupFailedError(error, schema, branchName) {
|
|
|
268
277
|
meta: {
|
|
269
278
|
branch: branchName,
|
|
270
279
|
schemaPath: schema.path,
|
|
280
|
+
source: schema.kind,
|
|
271
281
|
command: schema.command
|
|
272
282
|
},
|
|
273
283
|
exitCode: 1,
|
|
274
|
-
nextSteps: [schema
|
|
284
|
+
nextSteps: [...formatSchemaSetupNextSteps(schema, cwd), `prisma-cli app deploy --branch ${formatCommandArgument(branchName)} --db`]
|
|
275
285
|
});
|
|
276
286
|
}
|
|
287
|
+
function unsupportedBranchDatabaseSchemaError(schema, branchName, context) {
|
|
288
|
+
return usageError("Branch 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 for this branch, or switch the Prisma schema source to PostgreSQL before using --db.", [`prisma-cli project env add DATABASE_URL=<value> --branch ${formatCommandArgument(branchName)}`, `prisma-cli app deploy --branch ${formatCommandArgument(branchName)}`], "app");
|
|
289
|
+
}
|
|
290
|
+
function formatSchemaSetupNextSteps(schema, cwd) {
|
|
291
|
+
const sourcePath = path.relative(cwd, schema.path) || defaultSchemaSourcePath(schema);
|
|
292
|
+
switch (schema.command) {
|
|
293
|
+
case "migrate-deploy": return [`npx --no-install prisma migrate deploy --schema ${formatCommandArgument(sourcePath)}`];
|
|
294
|
+
case "db-push": return [`npx --no-install prisma db push --schema ${formatCommandArgument(sourcePath)}`];
|
|
295
|
+
case "prisma-next-db-init": return [`npx --no-install prisma-next contract emit --config ${formatCommandArgument(sourcePath)}`, `npx --no-install prisma-next db init --config ${formatCommandArgument(sourcePath)} --db <DATABASE_URL>`];
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
function defaultSchemaSourcePath(schema) {
|
|
299
|
+
return schema.kind === "prisma-next" ? "prisma-next.config.ts" : "schema.prisma";
|
|
300
|
+
}
|
|
301
|
+
function defaultUnsupportedSchemaSourcePath(schema) {
|
|
302
|
+
return schema.kind === "prisma-next" ? "prisma-next.config.ts" : "schema.prisma";
|
|
303
|
+
}
|
|
304
|
+
function formatUnsupportedSchemaTarget(target) {
|
|
305
|
+
switch (target) {
|
|
306
|
+
case "cockroachdb": return "CockroachDB";
|
|
307
|
+
case "mongodb": return "MongoDB";
|
|
308
|
+
case "mysql": return "MySQL";
|
|
309
|
+
case "sqlite": return "SQLite";
|
|
310
|
+
case "sqlserver": return "SQL Server";
|
|
311
|
+
}
|
|
312
|
+
}
|
|
277
313
|
function formatDebugDetails(error) {
|
|
278
314
|
if (error instanceof Error) return error.stack ?? error.message;
|
|
279
315
|
return typeof error === "string" ? error : null;
|
|
@@ -10,6 +10,7 @@ const SKIPPED_DIRECTORIES = new Set([
|
|
|
10
10
|
".prisma",
|
|
11
11
|
".turbo",
|
|
12
12
|
".vercel",
|
|
13
|
+
".wrangler",
|
|
13
14
|
"build",
|
|
14
15
|
"coverage",
|
|
15
16
|
"dist",
|
|
@@ -37,29 +38,41 @@ async function inspectBranchDatabaseSignal(cwd, signal) {
|
|
|
37
38
|
const state = {
|
|
38
39
|
filesVisited: 0,
|
|
39
40
|
schemaCandidates: [],
|
|
41
|
+
prismaNextConfigCandidates: [],
|
|
40
42
|
databaseUrlReferences: []
|
|
41
43
|
};
|
|
42
44
|
await scanDirectory(cwd, cwd, 0, state, signal);
|
|
43
|
-
const
|
|
44
|
-
const
|
|
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;
|
|
45
56
|
return {
|
|
46
|
-
schema
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
57
|
+
schema,
|
|
58
|
+
unsupportedSchema: schema ? null : unsupportedPrismaNextConfig ? {
|
|
59
|
+
kind: "prisma-next",
|
|
60
|
+
path: unsupportedPrismaNextConfig.path,
|
|
61
|
+
target: unsupportedPrismaNextConfig.target
|
|
62
|
+
} : selectedPrismaOrmSchema.unsupportedSchema,
|
|
51
63
|
databaseUrlReferences: state.databaseUrlReferences
|
|
52
64
|
};
|
|
53
65
|
}
|
|
54
66
|
function hasBranchDatabaseSignal(signal) {
|
|
67
|
+
if (signal.unsupportedSchema) return false;
|
|
55
68
|
return Boolean(signal.schema || signal.databaseUrlReferences.length > 0);
|
|
56
69
|
}
|
|
57
70
|
async function runBranchDatabaseSchemaSetup(options) {
|
|
58
|
-
const schemaPath = path.relative(options.context.runtime.cwd, options.schema.path) ||
|
|
59
|
-
const
|
|
60
|
-
await runPrismaCommand({
|
|
71
|
+
const schemaPath = path.relative(options.context.runtime.cwd, options.schema.path) || defaultSchemaSourcePath(options.schema);
|
|
72
|
+
const commands = buildSchemaSetupCommands(options.schema, schemaPath, options.databaseUrl);
|
|
73
|
+
for (const command of commands) await runPrismaCommand({
|
|
61
74
|
context: options.context,
|
|
62
|
-
|
|
75
|
+
...command,
|
|
63
76
|
env: {
|
|
64
77
|
DATABASE_URL: options.databaseUrl,
|
|
65
78
|
...options.directUrl ? { DIRECT_URL: options.directUrl } : {}
|
|
@@ -67,6 +80,7 @@ async function runBranchDatabaseSchemaSetup(options) {
|
|
|
67
80
|
});
|
|
68
81
|
return {
|
|
69
82
|
command: options.schema.command,
|
|
83
|
+
source: options.schema.kind,
|
|
70
84
|
schemaPath
|
|
71
85
|
};
|
|
72
86
|
}
|
|
@@ -92,18 +106,57 @@ async function scanDirectory(cwd, directory, depth, state, signal) {
|
|
|
92
106
|
if (!entry.isFile()) continue;
|
|
93
107
|
state.filesVisited += 1;
|
|
94
108
|
if (entry.name === "schema.prisma") state.schemaCandidates.push(entryPath);
|
|
109
|
+
if (isPrismaNextConfigFile(entry.name)) state.prismaNextConfigCandidates.push(entryPath);
|
|
95
110
|
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);
|
|
96
111
|
}
|
|
97
112
|
}
|
|
98
|
-
function
|
|
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) {
|
|
99
152
|
return candidates.map((candidate) => ({
|
|
100
153
|
absolute: candidate,
|
|
101
|
-
relative: path.relative(cwd, candidate) ||
|
|
154
|
+
relative: path.relative(cwd, candidate) || preferredRootFile
|
|
102
155
|
})).sort((left, right) => {
|
|
103
|
-
if (left.relative ===
|
|
104
|
-
if (right.relative ===
|
|
156
|
+
if (left.relative === preferredRootFile) return -1;
|
|
157
|
+
if (right.relative === preferredRootFile) return 1;
|
|
105
158
|
return left.relative.length - right.relative.length || left.relative.localeCompare(right.relative);
|
|
106
|
-
})
|
|
159
|
+
}).map((candidate) => candidate.absolute);
|
|
107
160
|
}
|
|
108
161
|
async function hasMigrationsDirectory(schemaDirectory, signal) {
|
|
109
162
|
signal.throwIfAborted();
|
|
@@ -116,36 +169,115 @@ async function hasMigrationsDirectory(schemaDirectory, signal) {
|
|
|
116
169
|
throw error;
|
|
117
170
|
}
|
|
118
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
|
+
async function classifyPrismaOrmSchemaTarget(schemaPath, signal) {
|
|
196
|
+
switch ((await readTextFileIfSmall(schemaPath, signal))?.match(/\bprovider\s*=\s*"([^"]+)"/)?.[1] ?? null) {
|
|
197
|
+
case "postgresql": return "postgresql";
|
|
198
|
+
case "mongodb": return "mongodb";
|
|
199
|
+
case "mysql": return "mysql";
|
|
200
|
+
case "sqlite": return "sqlite";
|
|
201
|
+
case "sqlserver": return "sqlserver";
|
|
202
|
+
case "cockroachdb": return "cockroachdb";
|
|
203
|
+
default: return "unknown";
|
|
204
|
+
}
|
|
205
|
+
}
|
|
119
206
|
function shouldScanForDatabaseUrl(fileName) {
|
|
120
207
|
if (fileName === ".env" || fileName.startsWith(".env.")) return true;
|
|
121
208
|
return DATABASE_URL_SCAN_EXTENSIONS.has(path.extname(fileName));
|
|
122
209
|
}
|
|
210
|
+
function isPrismaNextConfigFile(fileName) {
|
|
211
|
+
if (!fileName.startsWith("prisma-next.config.")) return false;
|
|
212
|
+
return [
|
|
213
|
+
".cjs",
|
|
214
|
+
".cts",
|
|
215
|
+
".js",
|
|
216
|
+
".mjs",
|
|
217
|
+
".mts",
|
|
218
|
+
".ts"
|
|
219
|
+
].some((extension) => fileName.endsWith(extension));
|
|
220
|
+
}
|
|
123
221
|
async function fileContainsDatabaseUrl(filePath, signal) {
|
|
222
|
+
return (await readTextFileIfSmall(filePath, signal))?.includes("DATABASE_URL") ?? false;
|
|
223
|
+
}
|
|
224
|
+
async function readTextFileIfSmall(filePath, signal) {
|
|
124
225
|
signal.throwIfAborted();
|
|
125
|
-
if ((await stat(filePath)).size > MAX_TEXT_FILE_BYTES) return
|
|
126
|
-
return
|
|
226
|
+
if ((await stat(filePath)).size > MAX_TEXT_FILE_BYTES) return null;
|
|
227
|
+
return readFile(filePath, {
|
|
127
228
|
encoding: "utf8",
|
|
128
229
|
signal
|
|
129
|
-
})
|
|
130
|
-
}
|
|
131
|
-
function
|
|
132
|
-
if (command === "migrate-deploy") return [
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
"--no-install"
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
function buildSchemaSetupCommands(schema, schemaPath, databaseUrl) {
|
|
233
|
+
if (schema.command === "migrate-deploy") return [{
|
|
234
|
+
args: [
|
|
235
|
+
"--no-install",
|
|
236
|
+
"prisma",
|
|
237
|
+
"migrate",
|
|
238
|
+
"deploy",
|
|
239
|
+
"--schema",
|
|
240
|
+
schemaPath
|
|
241
|
+
],
|
|
242
|
+
displayCommand: "npx --no-install prisma migrate deploy"
|
|
243
|
+
}];
|
|
244
|
+
if (schema.command === "db-push") return [{
|
|
245
|
+
args: [
|
|
246
|
+
"--no-install",
|
|
247
|
+
"prisma",
|
|
248
|
+
"db",
|
|
249
|
+
"push",
|
|
250
|
+
"--schema",
|
|
251
|
+
schemaPath
|
|
252
|
+
],
|
|
253
|
+
displayCommand: "npx --no-install prisma db push"
|
|
254
|
+
}];
|
|
255
|
+
return [{
|
|
256
|
+
args: [
|
|
257
|
+
"--no-install",
|
|
258
|
+
"prisma-next",
|
|
259
|
+
"contract",
|
|
260
|
+
"emit",
|
|
261
|
+
"--config",
|
|
262
|
+
schemaPath
|
|
263
|
+
],
|
|
264
|
+
displayCommand: "npx --no-install prisma-next contract emit"
|
|
265
|
+
}, {
|
|
266
|
+
args: [
|
|
267
|
+
"--no-install",
|
|
268
|
+
"prisma-next",
|
|
269
|
+
"db",
|
|
270
|
+
"init",
|
|
271
|
+
"--config",
|
|
272
|
+
schemaPath,
|
|
273
|
+
"--db",
|
|
274
|
+
databaseUrl
|
|
275
|
+
],
|
|
276
|
+
displayCommand: "npx --no-install prisma-next db init"
|
|
277
|
+
}];
|
|
278
|
+
}
|
|
279
|
+
function defaultSchemaSourcePath(schema) {
|
|
280
|
+
return schema.kind === "prisma-next" ? "prisma-next.config.ts" : "schema.prisma";
|
|
149
281
|
}
|
|
150
282
|
async function runPrismaCommand(options) {
|
|
151
283
|
const shouldPipeOutput = !options.context.flags.json && !options.context.flags.quiet;
|
|
@@ -177,8 +309,8 @@ async function runPrismaCommand(options) {
|
|
|
177
309
|
signal
|
|
178
310
|
}));
|
|
179
311
|
});
|
|
180
|
-
if (exit.signal) throw new Error(
|
|
181
|
-
if (exit.code !== 0) throw new Error(
|
|
312
|
+
if (exit.signal) throw new Error(`${options.displayCommand} was terminated by ${exit.signal}.`);
|
|
313
|
+
if (exit.code !== 0) throw new Error(`${options.displayCommand} exited with code ${exit.code ?? 1}.`);
|
|
182
314
|
}
|
|
183
315
|
//#endregion
|
|
184
316
|
export { hasBranchDatabaseSignal, inspectBranchDatabaseSignal, runBranchDatabaseSchemaSetup };
|
package/dist/presenters/app.js
CHANGED
|
@@ -55,10 +55,17 @@ function renderBranchDatabaseDeploySummary(context, result) {
|
|
|
55
55
|
},
|
|
56
56
|
...result.branchDatabase.schema ? [{
|
|
57
57
|
label: "Schema",
|
|
58
|
-
value: result.branchDatabase.schema.command
|
|
58
|
+
value: formatBranchDatabaseSchemaCommand(result.branchDatabase.schema.command)
|
|
59
59
|
}] : []
|
|
60
60
|
])];
|
|
61
61
|
}
|
|
62
|
+
function formatBranchDatabaseSchemaCommand(command) {
|
|
63
|
+
switch (command) {
|
|
64
|
+
case "migrate-deploy": return "prisma migrate deploy";
|
|
65
|
+
case "db-push": return "prisma db push";
|
|
66
|
+
case "prisma-next-db-init": return "prisma-next db init";
|
|
67
|
+
}
|
|
68
|
+
}
|
|
62
69
|
function formatDuration(durationMs) {
|
|
63
70
|
if (durationMs < 1e3) return `${durationMs}ms`;
|
|
64
71
|
return `${(durationMs / 1e3).toFixed(1)}s`;
|