@prisma/cli 3.0.0-alpha.0 → 3.0.0-alpha.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.
- package/README.md +1 -16
- package/dist/cli2.js +2 -0
- package/dist/commands/app/index.js +5 -11
- package/dist/commands/auth/index.js +2 -1
- package/dist/commands/branch/index.js +2 -1
- package/dist/commands/project/index.js +2 -1
- package/dist/controllers/app.js +98 -77
- package/dist/controllers/auth.js +7 -7
- package/dist/controllers/branch.js +5 -5
- package/dist/controllers/project.js +14 -14
- package/dist/lib/app/env-vars.js +4 -4
- package/dist/lib/app/local-dev.js +1 -0
- package/dist/lib/app/preview-build.js +45 -186
- package/dist/lib/auth/login.js +115 -4
- package/dist/output/patterns.js +14 -16
- package/dist/shell/command-meta.js +59 -81
- package/dist/shell/errors.js +2 -2
- package/dist/shell/global-flags.js +12 -1
- package/dist/shell/help.js +7 -6
- package/dist/use-cases/auth.js +4 -4
- package/dist/use-cases/create-cli-gateways.js +1 -1
- package/dist/use-cases/project.js +2 -2
- package/package.json +2 -2
|
@@ -24,7 +24,7 @@ async function runBranchShow(context) {
|
|
|
24
24
|
command: "branch.show",
|
|
25
25
|
result,
|
|
26
26
|
warnings: [],
|
|
27
|
-
nextSteps: result.branch.kind === "preview" && !result.branch.remoteState ? ["prisma app deploy"] : []
|
|
27
|
+
nextSteps: result.branch.kind === "preview" && !result.branch.remoteState ? ["prisma-cli app deploy"] : []
|
|
28
28
|
};
|
|
29
29
|
}
|
|
30
30
|
async function runBranchUse(context, branchName) {
|
|
@@ -37,7 +37,7 @@ async function runBranchUse(context, branchName) {
|
|
|
37
37
|
command: "branch.use",
|
|
38
38
|
result,
|
|
39
39
|
warnings: result.branch.kind === "production" ? ["Production is protected and durable. Use with care."] : [],
|
|
40
|
-
nextSteps: result.branch.kind === "preview" && !result.branch.remoteState ? ["prisma branch show", "prisma app deploy"] : ["prisma branch show"]
|
|
40
|
+
nextSteps: result.branch.kind === "preview" && !result.branch.remoteState ? ["prisma-cli branch show", "prisma-cli app deploy"] : ["prisma-cli branch show"]
|
|
41
41
|
};
|
|
42
42
|
}
|
|
43
43
|
async function resolveBranchNameForUse(context, useCases, branchName) {
|
|
@@ -61,13 +61,13 @@ function renderBranchChoiceLabel(branch) {
|
|
|
61
61
|
function validateBranchName(branchName) {
|
|
62
62
|
if (branchName === "production") return;
|
|
63
63
|
if (PREVIEW_BRANCH_PATTERN.test(branchName)) return;
|
|
64
|
-
throw usageError("Branch name must use the documented form", "Branch names must be production or a lowercase preview slug such as preview or feat-auth.", "Use production or a lowercase preview branch name with letters, numbers, and hyphens.", ["prisma branch list"], "branch");
|
|
64
|
+
throw usageError("Branch name must use the documented form", "Branch names must be production or a lowercase preview slug such as preview or feat-auth.", "Use production or a lowercase preview branch name with letters, numbers, and hyphens.", ["prisma-cli branch list"], "branch");
|
|
65
65
|
}
|
|
66
66
|
function branchSelectionRequiredError() {
|
|
67
|
-
return usageError("Branch use requires a target in non-interactive mode", "This command cannot prompt for branch selection in the current mode.", "Re-run prisma branch use in a TTY, or pass a branch name explicitly.", ["prisma branch list"], "branch");
|
|
67
|
+
return usageError("Branch use requires a target in non-interactive mode", "This command cannot prompt for branch selection in the current mode.", "Re-run prisma-cli branch use in a TTY, or pass a branch name explicitly.", ["prisma-cli branch list"], "branch");
|
|
68
68
|
}
|
|
69
69
|
function branchCommandsUnavailableError() {
|
|
70
|
-
return featureUnavailableError("Branch commands are not available in this preview", "The current preview cannot resolve or change remote branch context yet.", "Use prisma app deploy for preview app deployment workflows.", ["prisma app deploy --app <name>"], "branch");
|
|
70
|
+
return featureUnavailableError("Branch commands are not available in this preview", "The current preview cannot resolve or change remote branch context yet.", "Use prisma-cli app deploy for preview app deployment workflows.", ["prisma-cli app deploy --app <name>"], "branch");
|
|
71
71
|
}
|
|
72
72
|
//#endregion
|
|
73
73
|
export { runBranchList, runBranchShow, runBranchUse };
|
|
@@ -30,7 +30,7 @@ async function runProjectList(context) {
|
|
|
30
30
|
})).sort((left, right) => left.name.localeCompare(right.name) || left.id.localeCompare(right.id))
|
|
31
31
|
},
|
|
32
32
|
warnings: [],
|
|
33
|
-
nextSteps: ["prisma project link"]
|
|
33
|
+
nextSteps: ["prisma-cli project link"]
|
|
34
34
|
};
|
|
35
35
|
}
|
|
36
36
|
const authState = await requireAuthenticatedAuthState(context);
|
|
@@ -38,7 +38,7 @@ async function runProjectList(context) {
|
|
|
38
38
|
command: "project.list",
|
|
39
39
|
result: await createProjectUseCases(createCliUseCaseGateways(context)).list(authState),
|
|
40
40
|
warnings: [],
|
|
41
|
-
nextSteps: ["prisma project link"]
|
|
41
|
+
nextSteps: ["prisma-cli project link"]
|
|
42
42
|
};
|
|
43
43
|
}
|
|
44
44
|
async function runProjectShow(context) {
|
|
@@ -52,7 +52,7 @@ async function runProjectShow(context) {
|
|
|
52
52
|
project: null
|
|
53
53
|
},
|
|
54
54
|
warnings: [],
|
|
55
|
-
nextSteps: ["prisma project link"]
|
|
55
|
+
nextSteps: ["prisma-cli project link"]
|
|
56
56
|
};
|
|
57
57
|
const authState = await readAuthState(context.runtime.env);
|
|
58
58
|
if (!authState.authenticated || !authState.workspace) return {
|
|
@@ -63,7 +63,7 @@ async function runProjectShow(context) {
|
|
|
63
63
|
project: null
|
|
64
64
|
},
|
|
65
65
|
warnings: [],
|
|
66
|
-
nextSteps: ["prisma auth login"]
|
|
66
|
+
nextSteps: ["prisma-cli auth login"]
|
|
67
67
|
};
|
|
68
68
|
const client = await requireComputeAuth(context.runtime.env);
|
|
69
69
|
if (!client) return {
|
|
@@ -74,7 +74,7 @@ async function runProjectShow(context) {
|
|
|
74
74
|
project: null
|
|
75
75
|
},
|
|
76
76
|
warnings: [],
|
|
77
|
-
nextSteps: ["prisma auth login"]
|
|
77
|
+
nextSteps: ["prisma-cli auth login"]
|
|
78
78
|
};
|
|
79
79
|
try {
|
|
80
80
|
const { data } = await client.GET("/v1/projects/{id}", { params: { path: { id: linkedProjectId } } });
|
|
@@ -127,7 +127,7 @@ async function runProjectShow(context) {
|
|
|
127
127
|
command: "project.show",
|
|
128
128
|
result,
|
|
129
129
|
warnings: [],
|
|
130
|
-
nextSteps: result.linkedProjectId ? authState.authenticated ? [] : ["prisma auth login"] : ["prisma project link"]
|
|
130
|
+
nextSteps: result.linkedProjectId ? authState.authenticated ? [] : ["prisma-cli auth login"] : ["prisma-cli project link"]
|
|
131
131
|
};
|
|
132
132
|
}
|
|
133
133
|
async function runProjectLink(context, projectId) {
|
|
@@ -140,11 +140,11 @@ async function runProjectLink(context, projectId) {
|
|
|
140
140
|
let selectedProject;
|
|
141
141
|
if (projectId) try {
|
|
142
142
|
const { data } = await client.GET("/v1/projects/{id}", { params: { path: { id: projectId } } });
|
|
143
|
-
if (!data?.data || data.data.workspace.id !== workspace.id) throw projectNotFoundError(`The project "${projectId}" does not exist in workspace "${workspace.name}".`, "Run prisma project list and choose a project id from the active workspace.");
|
|
143
|
+
if (!data?.data || data.data.workspace.id !== workspace.id) throw projectNotFoundError(`The project "${projectId}" does not exist in workspace "${workspace.name}".`, "Run prisma-cli project list and choose a project id from the active workspace.");
|
|
144
144
|
selectedProject = data.data;
|
|
145
145
|
} catch (error) {
|
|
146
146
|
if (error instanceof CliError) throw error;
|
|
147
|
-
throw projectNotFoundError(`The project "${projectId}" does not exist in workspace "${workspace.name}".`, "Run prisma project list and choose a project id from the active workspace.");
|
|
147
|
+
throw projectNotFoundError(`The project "${projectId}" does not exist in workspace "${workspace.name}".`, "Run prisma-cli project list and choose a project id from the active workspace.");
|
|
148
148
|
}
|
|
149
149
|
else {
|
|
150
150
|
const { data: projectsData } = await client.GET("/v1/projects", {});
|
|
@@ -153,7 +153,7 @@ async function runProjectLink(context, projectId) {
|
|
|
153
153
|
name: project.name,
|
|
154
154
|
workspace: project.workspace
|
|
155
155
|
})).sort((left, right) => left.name.localeCompare(right.name) || left.id.localeCompare(right.id));
|
|
156
|
-
if (projects.length === 0) throw projectNotFoundError(`No projects are available in workspace "${workspace.name}".`, "Use prisma app deploy to create project context, or switch workspaces and try again.", []);
|
|
156
|
+
if (projects.length === 0) throw projectNotFoundError(`No projects are available in workspace "${workspace.name}".`, "Use prisma-cli app deploy to create project context, or switch workspaces and try again.", []);
|
|
157
157
|
selectedProject = await createSelectPromptPort(context).select({
|
|
158
158
|
message: "Select a project",
|
|
159
159
|
choices: projects.map((project) => ({
|
|
@@ -165,7 +165,7 @@ async function runProjectLink(context, projectId) {
|
|
|
165
165
|
try {
|
|
166
166
|
await writeLinkedProjectId(context.runtime.cwd, selectedProject.id);
|
|
167
167
|
} catch (error) {
|
|
168
|
-
if (error instanceof UnsafeConfigWriteError) throw usageError("Project link requires a writable Prisma config", error.message, "Update prisma.config.ts to use a recognizable project field, or remove it and rerun prisma project link.", ["prisma project link proj_123"], "project");
|
|
168
|
+
if (error instanceof UnsafeConfigWriteError) throw usageError("Project link requires a writable Prisma config", error.message, "Update prisma.config.ts to use a recognizable project field, or remove it and rerun prisma-cli project link.", ["prisma-cli project link proj_123"], "project");
|
|
169
169
|
throw error;
|
|
170
170
|
}
|
|
171
171
|
return {
|
|
@@ -182,7 +182,7 @@ async function runProjectLink(context, projectId) {
|
|
|
182
182
|
}
|
|
183
183
|
},
|
|
184
184
|
warnings: [],
|
|
185
|
-
nextSteps: ["prisma project show", "prisma app deploy"]
|
|
185
|
+
nextSteps: ["prisma-cli project show", "prisma-cli app deploy"]
|
|
186
186
|
};
|
|
187
187
|
}
|
|
188
188
|
const projectUseCases = createProjectUseCases(createCliUseCaseGateways(context));
|
|
@@ -192,13 +192,13 @@ async function runProjectLink(context, projectId) {
|
|
|
192
192
|
command: "project.link",
|
|
193
193
|
result: await projectUseCases.link(authState, resolvedProjectId),
|
|
194
194
|
warnings: [],
|
|
195
|
-
nextSteps: ["prisma project show", "prisma app deploy"]
|
|
195
|
+
nextSteps: ["prisma-cli project show", "prisma-cli app deploy"]
|
|
196
196
|
};
|
|
197
197
|
}
|
|
198
198
|
async function resolveProjectIdForLink(context, authState, projectUseCases) {
|
|
199
199
|
if (!authState.workspace) throw projectSelectionRequiredError();
|
|
200
200
|
const projects = await projectUseCases.listProjectsForWorkspace(authState.workspace.id);
|
|
201
|
-
if (projects.length === 0) throw projectNotFoundError(`No projects are available in workspace "${authState.workspace.name}".`, "Use prisma app deploy to create project context, or switch workspaces and try again.", []);
|
|
201
|
+
if (projects.length === 0) throw projectNotFoundError(`No projects are available in workspace "${authState.workspace.name}".`, "Use prisma-cli app deploy to create project context, or switch workspaces and try again.", []);
|
|
202
202
|
return (await createSelectPromptPort(context).select({
|
|
203
203
|
message: "Select a project",
|
|
204
204
|
choices: projects.map((project) => ({
|
|
@@ -208,7 +208,7 @@ async function resolveProjectIdForLink(context, authState, projectUseCases) {
|
|
|
208
208
|
})).id;
|
|
209
209
|
}
|
|
210
210
|
function projectSelectionRequiredError() {
|
|
211
|
-
return usageError("Project link requires a project target in non-interactive mode", "This command cannot prompt for project selection in the current mode.", "Re-run prisma project link in a TTY, or pass a project id explicitly.", ["prisma project list"], "project");
|
|
211
|
+
return usageError("Project link requires a project target in non-interactive mode", "This command cannot prompt for project selection in the current mode.", "Re-run prisma-cli project link in a TTY, or pass a project id explicitly.", ["prisma-cli project list"], "project");
|
|
212
212
|
}
|
|
213
213
|
//#endregion
|
|
214
214
|
export { runProjectLink, runProjectList, runProjectShow };
|
package/dist/lib/app/env-vars.js
CHANGED
|
@@ -2,15 +2,15 @@ import { usageError } from "../../shell/errors.js";
|
|
|
2
2
|
//#region src/lib/app/env-vars.ts
|
|
3
3
|
function parseEnvAssignments(assignments, options) {
|
|
4
4
|
const values = assignments ?? [];
|
|
5
|
-
if (options.requireAtLeastOne && values.length === 0) throw usageError("At least one environment variable is required", `prisma app ${options.commandName} needs at least one --env NAME=VALUE flag in the current mode.`, `Pass one or more --env NAME=VALUE flags, for example prisma app ${options.commandName} --env DATABASE_URL=postgresql://example.`, [`prisma app ${options.commandName} --env DATABASE_URL=postgresql://example`], "app");
|
|
5
|
+
if (options.requireAtLeastOne && values.length === 0) throw usageError("At least one environment variable is required", `prisma-cli app ${options.commandName} needs at least one --env NAME=VALUE flag in the current mode.`, `Pass one or more --env NAME=VALUE flags, for example prisma-cli app ${options.commandName} --env DATABASE_URL=postgresql://example.`, [`prisma-cli app ${options.commandName} --env DATABASE_URL=postgresql://example`], "app");
|
|
6
6
|
const parsed = {};
|
|
7
7
|
const seen = /* @__PURE__ */ new Set();
|
|
8
8
|
for (const assignment of values) {
|
|
9
9
|
const separatorIndex = assignment.indexOf("=");
|
|
10
|
-
if (separatorIndex === -1) throw usageError("Environment variable assignment must use NAME=VALUE", "A provided --env flag is missing the = separator.", `Pass repeated --env NAME=VALUE flags, for example prisma app ${options.commandName} --env DATABASE_URL=postgresql://example.`, [`prisma app ${options.commandName} --env DATABASE_URL=postgresql://example`], "app");
|
|
10
|
+
if (separatorIndex === -1) throw usageError("Environment variable assignment must use NAME=VALUE", "A provided --env flag is missing the = separator.", `Pass repeated --env NAME=VALUE flags, for example prisma-cli app ${options.commandName} --env DATABASE_URL=postgresql://example.`, [`prisma-cli app ${options.commandName} --env DATABASE_URL=postgresql://example`], "app");
|
|
11
11
|
const name = assignment.slice(0, separatorIndex);
|
|
12
|
-
if (name.length === 0) throw usageError("Environment variable name is required", "A provided --env flag has an empty variable name.", `Pass repeated --env NAME=VALUE flags, for example prisma app ${options.commandName} --env DATABASE_URL=postgresql://example.`, [`prisma app ${options.commandName} --env DATABASE_URL=postgresql://example`], "app");
|
|
13
|
-
if (seen.has(name)) throw usageError(`Environment variable "${name}" was provided more than once`, "Each environment variable name may be set only once per command invocation.", `Remove the duplicate "${name}" assignment and rerun prisma app ${options.commandName}.`, [`prisma app ${options.commandName} --env ${name}=value`], "app");
|
|
12
|
+
if (name.length === 0) throw usageError("Environment variable name is required", "A provided --env flag has an empty variable name.", `Pass repeated --env NAME=VALUE flags, for example prisma-cli app ${options.commandName} --env DATABASE_URL=postgresql://example.`, [`prisma-cli app ${options.commandName} --env DATABASE_URL=postgresql://example`], "app");
|
|
13
|
+
if (seen.has(name)) throw usageError(`Environment variable "${name}" was provided more than once`, "Each environment variable name may be set only once per command invocation.", `Remove the duplicate "${name}" assignment and rerun prisma-cli app ${options.commandName}.`, [`prisma-cli app ${options.commandName} --env ${name}=value`], "app");
|
|
14
14
|
seen.add(name);
|
|
15
15
|
parsed[name] = assignment.slice(separatorIndex + 1);
|
|
16
16
|
}
|
|
@@ -12,6 +12,7 @@ const NEXT_CONFIG_FILENAMES = [
|
|
|
12
12
|
const DEFAULT_LOCAL_DEV_PORT = 3e3;
|
|
13
13
|
async function resolveLocalBuildType(appPath, buildType) {
|
|
14
14
|
if (buildType === "bun" || buildType === "nextjs") return buildType;
|
|
15
|
+
if (buildType !== "auto") return null;
|
|
15
16
|
return detectLocalBuildType(appPath);
|
|
16
17
|
}
|
|
17
18
|
async function detectLocalBuildType(appPath) {
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import { resolveBunEntrypoint } from "./bun-project.js";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
import { execFile } from "node:child_process";
|
|
6
|
-
import { BunBuild } from "@prisma/compute-sdk";
|
|
3
|
+
import { cp, readdir, readlink, rm, stat } from "node:fs/promises";
|
|
4
|
+
import { AstroBuild, BunBuild, NextjsBuild, NuxtBuild, TanstackStartBuild } from "@prisma/compute-sdk";
|
|
7
5
|
//#region src/lib/app/preview-build.ts
|
|
8
|
-
const
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"
|
|
6
|
+
const PREVIEW_BUILD_TYPES = [
|
|
7
|
+
"auto",
|
|
8
|
+
"bun",
|
|
9
|
+
"nextjs",
|
|
10
|
+
"nuxt",
|
|
11
|
+
"astro",
|
|
12
|
+
"tanstack-start"
|
|
13
13
|
];
|
|
14
|
+
const RESOLVED_PREVIEW_BUILD_TYPES = PREVIEW_BUILD_TYPES.filter((buildType) => buildType !== "auto");
|
|
14
15
|
var PreviewBuildStrategy = class {
|
|
15
16
|
#appPath;
|
|
16
17
|
#entrypoint;
|
|
@@ -56,133 +57,52 @@ async function executePreviewBuild(options) {
|
|
|
56
57
|
}
|
|
57
58
|
}
|
|
58
59
|
async function resolvePreviewBuildStrategy(options) {
|
|
59
|
-
if (options.buildType
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
60
|
+
if (options.buildType !== "auto") {
|
|
61
|
+
const strategy = await createPreviewBuildStrategy({
|
|
62
|
+
appPath: options.appPath,
|
|
63
|
+
entrypoint: options.entrypoint,
|
|
64
|
+
buildType: options.buildType
|
|
65
|
+
});
|
|
65
66
|
return {
|
|
66
|
-
buildType:
|
|
67
|
-
strategy
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
67
|
+
buildType: options.buildType,
|
|
68
|
+
strategy
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
for (const buildType of RESOLVED_PREVIEW_BUILD_TYPES) {
|
|
72
|
+
if (buildType === "bun") continue;
|
|
73
|
+
const strategy = await createPreviewBuildStrategy({
|
|
74
|
+
appPath: options.appPath,
|
|
75
|
+
entrypoint: options.entrypoint,
|
|
76
|
+
buildType
|
|
77
|
+
});
|
|
78
|
+
if (await strategy.canBuild()) return {
|
|
79
|
+
buildType,
|
|
80
|
+
strategy
|
|
71
81
|
};
|
|
72
82
|
}
|
|
73
|
-
const nextjsStrategy = new PreviewNextjsBuild({ appPath: options.appPath });
|
|
74
|
-
if (await nextjsStrategy.canBuild()) return {
|
|
75
|
-
buildType: "nextjs",
|
|
76
|
-
strategy: nextjsStrategy
|
|
77
|
-
};
|
|
78
|
-
const entrypoint = await resolveBunEntrypoint(options.appPath, options.entrypoint);
|
|
79
83
|
return {
|
|
80
84
|
buildType: "bun",
|
|
81
|
-
strategy:
|
|
85
|
+
strategy: await createPreviewBuildStrategy({
|
|
82
86
|
appPath: options.appPath,
|
|
83
|
-
entrypoint
|
|
87
|
+
entrypoint: options.entrypoint,
|
|
88
|
+
buildType: "bun"
|
|
84
89
|
})
|
|
85
90
|
};
|
|
86
91
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
if (!(await stat(standaloneDir).catch(() => null))?.isDirectory()) throw new Error("Next.js build did not produce standalone output. Add output: \"standalone\" to your next.config file.");
|
|
99
|
-
const outDir = await mkdtemp(path.join(os.tmpdir(), "compute-build-"));
|
|
100
|
-
try {
|
|
101
|
-
const artifactDir = path.join(outDir, "app");
|
|
102
|
-
await stageNextjsStandaloneArtifact({
|
|
103
|
-
standaloneDir,
|
|
104
|
-
artifactDir,
|
|
105
|
-
appPath: this.#appPath
|
|
106
|
-
});
|
|
107
|
-
const publicDir = path.join(this.#appPath, "public");
|
|
108
|
-
if (await directoryExists(publicDir)) await cp(publicDir, path.join(artifactDir, "public"), { recursive: true });
|
|
109
|
-
const staticDir = path.join(this.#appPath, ".next", "static");
|
|
110
|
-
if (await directoryExists(staticDir)) await cp(staticDir, path.join(artifactDir, ".next", "static"), { recursive: true });
|
|
111
|
-
return {
|
|
112
|
-
directory: artifactDir,
|
|
113
|
-
entrypoint: "server.js",
|
|
114
|
-
defaultPortMapping: { http: 3e3 },
|
|
115
|
-
cleanup: () => rm(outDir, {
|
|
116
|
-
recursive: true,
|
|
117
|
-
force: true
|
|
118
|
-
})
|
|
119
|
-
};
|
|
120
|
-
} catch (error) {
|
|
121
|
-
await rm(outDir, {
|
|
122
|
-
recursive: true,
|
|
123
|
-
force: true
|
|
92
|
+
async function createPreviewBuildStrategy(options) {
|
|
93
|
+
switch (options.buildType) {
|
|
94
|
+
case "nextjs": return new NextjsBuild({ appPath: options.appPath });
|
|
95
|
+
case "nuxt": return new NuxtBuild({ appPath: options.appPath });
|
|
96
|
+
case "astro": return new AstroBuild({ appPath: options.appPath });
|
|
97
|
+
case "tanstack-start": return new TanstackStartBuild({ appPath: options.appPath });
|
|
98
|
+
case "bun": {
|
|
99
|
+
const entrypoint = await resolveBunEntrypoint(options.appPath, options.entrypoint);
|
|
100
|
+
return new BunBuild({
|
|
101
|
+
appPath: options.appPath,
|
|
102
|
+
entrypoint
|
|
124
103
|
});
|
|
125
|
-
throw error;
|
|
126
104
|
}
|
|
127
105
|
}
|
|
128
|
-
async #hasNextConfig() {
|
|
129
|
-
let entries;
|
|
130
|
-
try {
|
|
131
|
-
entries = await readdir(this.#appPath);
|
|
132
|
-
} catch {
|
|
133
|
-
return false;
|
|
134
|
-
}
|
|
135
|
-
return entries.some((entry) => NEXT_CONFIG_FILENAMES.includes(entry));
|
|
136
|
-
}
|
|
137
|
-
async #hasNextDependency() {
|
|
138
|
-
const packageJsonPath = path.join(this.#appPath, "package.json");
|
|
139
|
-
let content;
|
|
140
|
-
try {
|
|
141
|
-
content = await readFile(packageJsonPath, "utf8");
|
|
142
|
-
} catch {
|
|
143
|
-
return false;
|
|
144
|
-
}
|
|
145
|
-
let parsed;
|
|
146
|
-
try {
|
|
147
|
-
parsed = JSON.parse(content);
|
|
148
|
-
} catch {
|
|
149
|
-
return false;
|
|
150
|
-
}
|
|
151
|
-
const deps = isRecord(parsed.dependencies) ? parsed.dependencies : {};
|
|
152
|
-
const devDeps = isRecord(parsed.devDependencies) ? parsed.devDependencies : {};
|
|
153
|
-
return "next" in deps || "next" in devDeps;
|
|
154
|
-
}
|
|
155
|
-
async #runBuild() {
|
|
156
|
-
const candidates = [
|
|
157
|
-
{
|
|
158
|
-
command: path.join(this.#appPath, "node_modules", ".bin", "next"),
|
|
159
|
-
args: ["build"]
|
|
160
|
-
},
|
|
161
|
-
{
|
|
162
|
-
command: "npx",
|
|
163
|
-
args: ["next", "build"]
|
|
164
|
-
},
|
|
165
|
-
{
|
|
166
|
-
command: "bunx",
|
|
167
|
-
args: ["next", "build"]
|
|
168
|
-
}
|
|
169
|
-
];
|
|
170
|
-
for (const { command, args } of candidates) try {
|
|
171
|
-
await exec(command, args, this.#appPath);
|
|
172
|
-
return;
|
|
173
|
-
} catch (error) {
|
|
174
|
-
if (error instanceof Error && "code" in error && error.code === "ENOENT") continue;
|
|
175
|
-
throw error;
|
|
176
|
-
}
|
|
177
|
-
throw new Error("Could not find the Next.js CLI. Install it with `npm install next` or ensure npx/bunx is available.");
|
|
178
|
-
}
|
|
179
|
-
};
|
|
180
|
-
async function stageNextjsStandaloneArtifact(options) {
|
|
181
|
-
const standaloneRoot = path.resolve(options.standaloneDir);
|
|
182
|
-
await copyPathMaterializingSymlinks(standaloneRoot, path.resolve(options.artifactDir), {
|
|
183
|
-
standaloneRoot,
|
|
184
|
-
appRoot: path.resolve(options.appPath)
|
|
185
|
-
});
|
|
186
106
|
}
|
|
187
107
|
async function normalizeArtifactSymlinks(artifactDir, appPath) {
|
|
188
108
|
const normalizedArtifactDir = path.resolve(artifactDir);
|
|
@@ -218,66 +138,5 @@ function isPathWithin(rootPath, candidatePath) {
|
|
|
218
138
|
const relativePath = path.relative(rootPath, candidatePath);
|
|
219
139
|
return relativePath === "" || !relativePath.startsWith(`..${path.sep}`) && relativePath !== ".." && !path.isAbsolute(relativePath);
|
|
220
140
|
}
|
|
221
|
-
async function copyPathMaterializingSymlinks(sourcePath, destinationPath, options) {
|
|
222
|
-
const sourceStat = await lstat(sourcePath);
|
|
223
|
-
if (sourceStat.isSymbolicLink()) {
|
|
224
|
-
await copyPathMaterializingSymlinks(await resolveSymlinkTarget(sourcePath, options), destinationPath, options);
|
|
225
|
-
return;
|
|
226
|
-
}
|
|
227
|
-
if (sourceStat.isDirectory()) {
|
|
228
|
-
await mkdir(destinationPath, { recursive: true });
|
|
229
|
-
const entries = await readdir(sourcePath, { withFileTypes: true });
|
|
230
|
-
for (const entry of entries) await copyPathMaterializingSymlinks(path.join(sourcePath, entry.name), path.join(destinationPath, entry.name), options);
|
|
231
|
-
return;
|
|
232
|
-
}
|
|
233
|
-
if (sourceStat.isFile()) {
|
|
234
|
-
await mkdir(path.dirname(destinationPath), { recursive: true });
|
|
235
|
-
await copyFile(sourcePath, destinationPath);
|
|
236
|
-
await chmod(destinationPath, sourceStat.mode);
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
async function resolveSymlinkTarget(symlinkPath, options) {
|
|
240
|
-
const linkTarget = await readlink(symlinkPath);
|
|
241
|
-
const resolvedTarget = path.resolve(path.dirname(symlinkPath), linkTarget);
|
|
242
|
-
if (await pathExists(resolvedTarget)) {
|
|
243
|
-
if (!isPathWithin(options.appRoot, resolvedTarget)) throw new Error(`Build artifact symlink escapes the app directory: ${resolvedTarget}`);
|
|
244
|
-
return resolvedTarget;
|
|
245
|
-
}
|
|
246
|
-
if (isPathWithin(options.standaloneRoot, resolvedTarget)) {
|
|
247
|
-
const fallbackTarget = path.join(options.appRoot, path.relative(options.standaloneRoot, resolvedTarget));
|
|
248
|
-
if (await pathExists(fallbackTarget)) return fallbackTarget;
|
|
249
|
-
}
|
|
250
|
-
throw new Error(`Next.js standalone symlink target is missing: ${symlinkPath} -> ${linkTarget} (resolved to ${resolvedTarget})`);
|
|
251
|
-
}
|
|
252
|
-
async function directoryExists(dirPath) {
|
|
253
|
-
return (await stat(dirPath).catch(() => null))?.isDirectory() ?? false;
|
|
254
|
-
}
|
|
255
|
-
function exec(command, args, cwd) {
|
|
256
|
-
return new Promise((resolve, reject) => {
|
|
257
|
-
execFile(command, args, { cwd }, (error, _stdout, stderr) => {
|
|
258
|
-
if (error) {
|
|
259
|
-
if ("code" in error && error.code === "ENOENT") {
|
|
260
|
-
reject(Object.assign(/* @__PURE__ */ new Error(`${command} not found`), { code: "ENOENT" }));
|
|
261
|
-
return;
|
|
262
|
-
}
|
|
263
|
-
const message = stderr.trim() || error.message;
|
|
264
|
-
reject(/* @__PURE__ */ new Error(`Next.js build failed:\n${message}`));
|
|
265
|
-
return;
|
|
266
|
-
}
|
|
267
|
-
resolve();
|
|
268
|
-
});
|
|
269
|
-
});
|
|
270
|
-
}
|
|
271
|
-
function isRecord(value) {
|
|
272
|
-
return typeof value === "object" && value !== null;
|
|
273
|
-
}
|
|
274
|
-
async function pathExists(targetPath) {
|
|
275
|
-
try {
|
|
276
|
-
await stat(targetPath);
|
|
277
|
-
return true;
|
|
278
|
-
} catch {
|
|
279
|
-
return false;
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
141
|
//#endregion
|
|
283
|
-
export { PreviewBuildStrategy, executePreviewBuild };
|
|
142
|
+
export { PREVIEW_BUILD_TYPES, PreviewBuildStrategy, RESOLVED_PREVIEW_BUILD_TYPES, executePreviewBuild };
|
package/dist/lib/auth/login.js
CHANGED
|
@@ -47,8 +47,9 @@ async function login(options = {}) {
|
|
|
47
47
|
reject(error);
|
|
48
48
|
return;
|
|
49
49
|
}
|
|
50
|
-
|
|
51
|
-
res.
|
|
50
|
+
const workspaceName = await state.resolveWorkspaceName();
|
|
51
|
+
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
52
|
+
res.end(renderSuccessPage(workspaceName));
|
|
52
53
|
resolve();
|
|
53
54
|
});
|
|
54
55
|
});
|
|
@@ -63,13 +64,14 @@ var LoginState = class {
|
|
|
63
64
|
latestState;
|
|
64
65
|
sdk;
|
|
65
66
|
openUrl;
|
|
67
|
+
tokenStorage;
|
|
66
68
|
constructor(options) {
|
|
67
69
|
this.options = options;
|
|
68
|
-
|
|
70
|
+
this.tokenStorage = options.tokenStorage ?? new FileTokenStorage(options.env);
|
|
69
71
|
this.sdk = createManagementApiSdk({
|
|
70
72
|
clientId: options.clientId ?? "cmm3lndn701oo0uefvxzo0ivw",
|
|
71
73
|
redirectUri: `http://${options.hostname}:${options.port}/auth/callback`,
|
|
72
|
-
tokenStorage,
|
|
74
|
+
tokenStorage: this.tokenStorage,
|
|
73
75
|
apiBaseUrl: options.apiBaseUrl ?? getApiBaseUrl(options.env),
|
|
74
76
|
authBaseUrl: options.authBaseUrl
|
|
75
77
|
});
|
|
@@ -109,9 +111,118 @@ var LoginState = class {
|
|
|
109
111
|
throw new AuthError$1(error instanceof Error ? error.message : "Unknown error during login");
|
|
110
112
|
}
|
|
111
113
|
}
|
|
114
|
+
async resolveWorkspaceName() {
|
|
115
|
+
try {
|
|
116
|
+
const tokens = await this.tokenStorage.getTokens();
|
|
117
|
+
if (!tokens?.workspaceId) return null;
|
|
118
|
+
const { data } = await this.sdk.client.GET("/v1/workspaces/{id}", { params: { path: { id: tokens.workspaceId } } });
|
|
119
|
+
const name = data?.data?.name;
|
|
120
|
+
return typeof name === "string" && name.trim().length > 0 ? name.trim() : null;
|
|
121
|
+
} catch {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
112
125
|
get host() {
|
|
113
126
|
return `${this.options.hostname}:${this.options.port}`;
|
|
114
127
|
}
|
|
115
128
|
};
|
|
129
|
+
function renderSuccessPage(workspaceName) {
|
|
130
|
+
return `<!doctype html>
|
|
131
|
+
<html lang="en">
|
|
132
|
+
<head>
|
|
133
|
+
<meta charset="utf-8">
|
|
134
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
135
|
+
<title>Prisma Developer Platform</title>
|
|
136
|
+
<style>
|
|
137
|
+
:root {
|
|
138
|
+
color-scheme: light dark;
|
|
139
|
+
--background: #ffffff;
|
|
140
|
+
--foreground: #1f2430;
|
|
141
|
+
--muted: #4f5665;
|
|
142
|
+
--mark-color: #050812;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
@media (prefers-color-scheme: dark) {
|
|
146
|
+
:root {
|
|
147
|
+
--background: #050812;
|
|
148
|
+
--foreground: #f6f7fb;
|
|
149
|
+
--muted: #c5cad6;
|
|
150
|
+
--mark-color: #ffffff;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
* {
|
|
155
|
+
box-sizing: border-box;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
body {
|
|
159
|
+
min-height: 100vh;
|
|
160
|
+
margin: 0;
|
|
161
|
+
background: var(--background);
|
|
162
|
+
color: var(--foreground);
|
|
163
|
+
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
164
|
+
display: grid;
|
|
165
|
+
grid-template-rows: 128px 1fr;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.mark {
|
|
169
|
+
align-self: end;
|
|
170
|
+
justify-self: center;
|
|
171
|
+
width: 36px;
|
|
172
|
+
height: 36px;
|
|
173
|
+
color: var(--mark-color);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.mark path {
|
|
177
|
+
fill: currentColor !important;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
main {
|
|
181
|
+
align-self: center;
|
|
182
|
+
justify-self: center;
|
|
183
|
+
width: min(520px, calc(100vw - 48px));
|
|
184
|
+
margin-top: -128px;
|
|
185
|
+
text-align: center;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
h1 {
|
|
189
|
+
margin: 0 0 12px;
|
|
190
|
+
font-size: 26px;
|
|
191
|
+
line-height: 1.2;
|
|
192
|
+
font-weight: 700;
|
|
193
|
+
letter-spacing: 0;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
p {
|
|
197
|
+
margin: 0 auto;
|
|
198
|
+
max-width: 480px;
|
|
199
|
+
color: var(--muted);
|
|
200
|
+
font-size: 15px;
|
|
201
|
+
line-height: 1.55;
|
|
202
|
+
letter-spacing: 0;
|
|
203
|
+
}
|
|
204
|
+
</style>
|
|
205
|
+
</head>
|
|
206
|
+
<body>
|
|
207
|
+
<svg class="mark" width="36" height="36" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"><path d="M25.21,24.21,12.739,27.928a.525.525,0,0,1-.667-.606L16.528,5.811a.43.43,0,0,1,.809-.094l8.249,17.661A.6.6,0,0,1,25.21,24.21Zm2.139-.878L17.8,2.883h0A1.531,1.531,0,0,0,16.491,2a1.513,1.513,0,0,0-1.4.729L4.736,19.648a1.592,1.592,0,0,0,.018,1.7l5.064,7.909a1.628,1.628,0,0,0,1.83.678l14.7-4.383a1.6,1.6,0,0,0,1-2.218Z" style="fill:#0c344b;fill-rule:evenodd"/></svg>
|
|
208
|
+
<main>
|
|
209
|
+
<h1>You're all set.</h1>
|
|
210
|
+
<p>${workspaceName ? `Your terminal is now connected to your ${escapeHtml(workspaceName)} workspace. Head back to your terminal to continue.` : "Your terminal is now connected to your Prisma workspace. Head back to your terminal to continue."}</p>
|
|
211
|
+
</main>
|
|
212
|
+
</body>
|
|
213
|
+
</html>`;
|
|
214
|
+
}
|
|
215
|
+
function escapeHtml(value) {
|
|
216
|
+
return value.replace(/[&<>"']/g, (char) => {
|
|
217
|
+
switch (char) {
|
|
218
|
+
case "&": return "&";
|
|
219
|
+
case "<": return "<";
|
|
220
|
+
case ">": return ">";
|
|
221
|
+
case "\"": return """;
|
|
222
|
+
case "'": return "'";
|
|
223
|
+
default: return char;
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
}
|
|
116
227
|
//#endregion
|
|
117
228
|
export { login };
|
package/dist/output/patterns.js
CHANGED
|
@@ -3,13 +3,12 @@ import { maskValue, padDisplay, renderSummaryLine } from "../shell/ui.js";
|
|
|
3
3
|
import stringWidth from "string-width";
|
|
4
4
|
//#region src/output/patterns.ts
|
|
5
5
|
function renderList(input, ui) {
|
|
6
|
-
const keyWidth = Math.max(stringWidth(`${input.parentContext.key}:`), ...input.items.map((item) => stringWidth(`⚬ ${item.noun}:`)),
|
|
6
|
+
const keyWidth = Math.max(stringWidth(`${input.parentContext.key}:`), ...input.items.map((item) => stringWidth(`⚬ ${item.noun}:`)), ...readMoreWidth(input.descriptor));
|
|
7
7
|
const lines = renderCardTitle(input.descriptor, input.title, ui);
|
|
8
8
|
lines.push(renderCardRow(ui, keyWidth, input.parentContext.key, input.parentContext.value));
|
|
9
9
|
if (input.items.length === 0) lines.push(renderPlainCardLine(ui, ui.dim(input.emptyMessage)));
|
|
10
10
|
else for (const item of input.items) lines.push(renderCardRow(ui, keyWidth, `⚬ ${item.noun}`, formatListItemValue(ui, item)));
|
|
11
|
-
lines.
|
|
12
|
-
lines.push(renderReadMore(ui, keyWidth, input.descriptor));
|
|
11
|
+
pushReadMore(lines, ui, keyWidth, input.descriptor);
|
|
13
12
|
return lines;
|
|
14
13
|
}
|
|
15
14
|
function serializeList(input) {
|
|
@@ -24,24 +23,18 @@ function serializeList(input) {
|
|
|
24
23
|
};
|
|
25
24
|
}
|
|
26
25
|
function renderShow(input, ui) {
|
|
27
|
-
const keyWidth = Math.max(...input.fields.map((field) => stringWidth(`${field.key}:`)),
|
|
26
|
+
const keyWidth = Math.max(0, ...input.fields.map((field) => stringWidth(`${field.key}:`)), ...readMoreWidth(input.descriptor));
|
|
28
27
|
const lines = renderCardTitle(input.descriptor, input.title, ui);
|
|
29
28
|
for (const field of input.fields) lines.push(renderCardRow(ui, keyWidth, field.key, formatValue(ui, field.value, field.tone, field.sensitive)));
|
|
30
|
-
lines.
|
|
31
|
-
lines.push(renderReadMore(ui, keyWidth, input.descriptor));
|
|
29
|
+
pushReadMore(lines, ui, keyWidth, input.descriptor);
|
|
32
30
|
return lines;
|
|
33
31
|
}
|
|
34
32
|
function renderMutate(input, ui) {
|
|
35
|
-
const rows =
|
|
36
|
-
|
|
37
|
-
value: "apply",
|
|
38
|
-
tone: "dim"
|
|
39
|
-
}];
|
|
40
|
-
const keyWidth = Math.max(...rows.map((row) => stringWidth(`${row.key}:`)), stringWidth("Read more"));
|
|
33
|
+
const rows = input.context;
|
|
34
|
+
const keyWidth = Math.max(0, ...rows.map((row) => stringWidth(`${row.key}:`)), ...readMoreWidth(input.descriptor));
|
|
41
35
|
const lines = renderCardTitle(input.descriptor, input.title, ui);
|
|
42
36
|
for (const row of rows) lines.push(renderCardRow(ui, keyWidth, row.key, formatValue(ui, row.value, row.tone, row.sensitive)));
|
|
43
|
-
lines.
|
|
44
|
-
lines.push(renderReadMore(ui, keyWidth, input.descriptor));
|
|
37
|
+
pushReadMore(lines, ui, keyWidth, input.descriptor);
|
|
45
38
|
lines.push("");
|
|
46
39
|
lines.push(`${ui.warning("◇")} ${input.operationDescription}...`);
|
|
47
40
|
lines.push(renderSummaryLine(ui, "success", `Applied ${input.operationCount} operation(s)`));
|
|
@@ -64,8 +57,13 @@ function renderPlainCardLine(ui, text) {
|
|
|
64
57
|
function renderCardDivider(ui) {
|
|
65
58
|
return renderCardRail(ui);
|
|
66
59
|
}
|
|
67
|
-
function
|
|
68
|
-
return
|
|
60
|
+
function readMoreWidth(descriptor) {
|
|
61
|
+
return descriptor.docsPath ? [stringWidth("Read more")] : [];
|
|
62
|
+
}
|
|
63
|
+
function pushReadMore(lines, ui, keyWidth, descriptor) {
|
|
64
|
+
if (!descriptor.docsPath) return;
|
|
65
|
+
lines.push(renderCardDivider(ui));
|
|
66
|
+
lines.push(`${renderCardRail(ui)} ${ui.accent(padDisplay("Read more", keyWidth))} ${ui.link(descriptor.docsPath)}`);
|
|
69
67
|
}
|
|
70
68
|
function renderCardRail(ui) {
|
|
71
69
|
return ui.dim("│");
|