@prisma/cli 3.0.0-alpha.1 → 3.0.0-alpha.3

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 CHANGED
@@ -18,22 +18,7 @@ pnpm prisma-cli app list-env
18
18
  ```
19
19
 
20
20
  The package exposes `prisma-cli` so it can coexist with the existing `prisma`
21
- executable. If you want local project scripts that use the future command shape,
22
- add:
23
-
24
- ```json
25
- {
26
- "scripts": {
27
- "prisma": "prisma-cli"
28
- }
29
- }
30
- ```
31
-
32
- Then run:
33
-
34
- ```bash
35
- pnpm prisma app deploy
36
- ```
21
+ executable.
37
22
 
38
23
  Notes:
39
24
 
package/dist/cli2.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { attachCommandDescriptor } from "./shell/command-meta.js";
2
+ import { addCompactGlobalFlags } from "./shell/global-flags.js";
2
3
  import { configureRuntimeCommand } from "./shell/runtime.js";
3
4
  import "./shell/prompt.js";
4
5
  import { createAppCommand } from "./commands/app/index.js";
@@ -31,6 +32,7 @@ async function runCli(options = {}) {
31
32
  }
32
33
  function createProgram(runtime) {
33
34
  const program = attachCommandDescriptor(configureRuntimeCommand(new Command(), runtime), "root");
35
+ addCompactGlobalFlags(program);
34
36
  program.name("prisma").showSuggestionAfterError();
35
37
  program.addCommand(createAuthCommand(runtime));
36
38
  program.addCommand(createBranchCommand(runtime));
@@ -1,14 +1,15 @@
1
1
  import { attachCommandDescriptor } from "../../shell/command-meta.js";
2
- import { addGlobalFlags } from "../../shell/global-flags.js";
2
+ import { addCompactGlobalFlags, addGlobalFlags } from "../../shell/global-flags.js";
3
3
  import { configureRuntimeCommand } from "../../shell/runtime.js";
4
4
  import { PREVIEW_BUILD_TYPES } from "../../lib/app/preview-build.js";
5
5
  import { runAppBuild, runAppDeploy, runAppListDeploys, runAppListEnv, runAppLogs, runAppOpen, runAppPromote, runAppRemove, runAppRollback, runAppRun, runAppShow, runAppShowDeploy, runAppUpdateEnv } from "../../controllers/app.js";
6
6
  import { renderAppBuild, renderAppDeploy, renderAppListDeploys, renderAppListEnv, renderAppOpen, renderAppPromote, renderAppRemove, renderAppRollback, renderAppRun, renderAppShow, renderAppShowDeploy, renderAppUpdateEnv, serializeAppBuild, serializeAppDeploy, serializeAppListDeploys, serializeAppListEnv, serializeAppOpen, serializeAppPromote, serializeAppRemove, serializeAppRollback, serializeAppRun, serializeAppShow, serializeAppShowDeploy, serializeAppUpdateEnv } from "../../presenters/app.js";
7
- import { runCommand } from "../../shell/command-runner.js";
7
+ import { runCommand, runStreamingCommand } from "../../shell/command-runner.js";
8
8
  import { Command, Option } from "commander";
9
9
  //#region src/commands/app/index.ts
10
10
  function createAppCommand(runtime) {
11
11
  const app = attachCommandDescriptor(configureRuntimeCommand(new Command("app"), runtime), "app");
12
+ addCompactGlobalFlags(app);
12
13
  app.addCommand(createBuildCommand(runtime));
13
14
  app.addCommand(createRunCommand(runtime));
14
15
  app.addCommand(createDeployCommand(runtime));
@@ -139,7 +140,7 @@ function createLogsCommand(runtime) {
139
140
  command.action(async (options) => {
140
141
  const appName = options.app;
141
142
  const deploymentId = options.deployment;
142
- await runCommand(runtime, "app.logs", options, (context) => runAppLogs(context, appName, deploymentId), { renderHuman: () => [] });
143
+ await runStreamingCommand(runtime, "app.logs", options, (context) => runAppLogs(context, appName, deploymentId));
143
144
  });
144
145
  return command;
145
146
  }
@@ -1,5 +1,5 @@
1
1
  import { attachCommandDescriptor } from "../../shell/command-meta.js";
2
- import { addGlobalFlags } from "../../shell/global-flags.js";
2
+ import { addCompactGlobalFlags, addGlobalFlags } from "../../shell/global-flags.js";
3
3
  import { configureRuntimeCommand } from "../../shell/runtime.js";
4
4
  import { runCommand } from "../../shell/command-runner.js";
5
5
  import { runAuthLogin, runAuthLogout, runAuthWhoAmI } from "../../controllers/auth.js";
@@ -8,6 +8,7 @@ import { Command, Option } from "commander";
8
8
  //#region src/commands/auth/index.ts
9
9
  function createAuthCommand(runtime) {
10
10
  const auth = attachCommandDescriptor(configureRuntimeCommand(new Command("auth"), runtime), "auth");
11
+ addCompactGlobalFlags(auth);
11
12
  auth.addCommand(createAuthLoginCommand(runtime));
12
13
  auth.addCommand(createAuthLogoutCommand(runtime));
13
14
  auth.addCommand(createAuthWhoAmICommand(runtime));
@@ -1,5 +1,5 @@
1
1
  import { attachCommandDescriptor } from "../../shell/command-meta.js";
2
- import { addGlobalFlags } from "../../shell/global-flags.js";
2
+ import { addCompactGlobalFlags, addGlobalFlags } from "../../shell/global-flags.js";
3
3
  import { configureRuntimeCommand } from "../../shell/runtime.js";
4
4
  import { runCommand } from "../../shell/command-runner.js";
5
5
  import { runBranchList, runBranchShow, runBranchUse } from "../../controllers/branch.js";
@@ -8,6 +8,7 @@ import { Command } from "commander";
8
8
  //#region src/commands/branch/index.ts
9
9
  function createBranchCommand(runtime) {
10
10
  const branch = attachCommandDescriptor(configureRuntimeCommand(new Command("branch"), runtime), "branch");
11
+ addCompactGlobalFlags(branch);
11
12
  branch.addCommand(createBranchListCommand(runtime));
12
13
  branch.addCommand(createBranchShowCommand(runtime));
13
14
  branch.addCommand(createBranchUseCommand(runtime));
@@ -0,0 +1,71 @@
1
+ import { attachCommandDescriptor } from "../shell/command-meta.js";
2
+ import { addGlobalFlags } from "../shell/global-flags.js";
3
+ import { configureRuntimeCommand } from "../shell/runtime.js";
4
+ import { runCommand } from "../shell/command-runner.js";
5
+ import { runEnvAdd, runEnvList, runEnvRm, runEnvUpdate } from "../controllers/app-env.js";
6
+ import { renderEnvAdd, renderEnvList, renderEnvRm, renderEnvUpdate, serializeEnvAdd, serializeEnvList, serializeEnvRm, serializeEnvUpdate } from "../presenters/app-env.js";
7
+ import { Command, Option } from "commander";
8
+ //#region src/commands/env.ts
9
+ function createEnvCommand(runtime) {
10
+ const env = attachCommandDescriptor(configureRuntimeCommand(new Command("env"), runtime), "project.env");
11
+ env.description("Manage environment variables for the linked project.");
12
+ env.addCommand(createEnvAddCommand(runtime));
13
+ env.addCommand(createEnvUpdateCommand(runtime));
14
+ env.addCommand(createEnvListCommand(runtime));
15
+ env.addCommand(createEnvRmCommand(runtime));
16
+ return env;
17
+ }
18
+ function createEnvAddCommand(runtime) {
19
+ const command = attachCommandDescriptor(configureRuntimeCommand(new Command("add"), runtime), "project.env.add");
20
+ command.argument("<assignment>", "Variable assignment in KEY=VALUE form").addOption(new Option("--role <role>", "Project template scope (production or preview)").choices(["production", "preview"]));
21
+ addGlobalFlags(command);
22
+ command.action(async (assignment, options) => {
23
+ const roleName = options.role;
24
+ await runCommand(runtime, "project.env.add", options, (context) => runEnvAdd(context, assignment, { roleName }), {
25
+ renderHuman: (context, descriptor, result) => renderEnvAdd(context, descriptor, result),
26
+ renderJson: (result) => serializeEnvAdd(result)
27
+ });
28
+ });
29
+ return command;
30
+ }
31
+ function createEnvUpdateCommand(runtime) {
32
+ const command = attachCommandDescriptor(configureRuntimeCommand(new Command("update"), runtime), "project.env.update");
33
+ command.argument("<assignment>", "Variable assignment in KEY=VALUE form").addOption(new Option("--role <role>", "Project template scope (production or preview)").choices(["production", "preview"]));
34
+ addGlobalFlags(command);
35
+ command.action(async (assignment, options) => {
36
+ const roleName = options.role;
37
+ await runCommand(runtime, "project.env.update", options, (context) => runEnvUpdate(context, assignment, { roleName }), {
38
+ renderHuman: (context, descriptor, result) => renderEnvUpdate(context, descriptor, result),
39
+ renderJson: (result) => serializeEnvUpdate(result)
40
+ });
41
+ });
42
+ return command;
43
+ }
44
+ function createEnvListCommand(runtime) {
45
+ const command = attachCommandDescriptor(configureRuntimeCommand(new Command("list"), runtime), "project.env.list");
46
+ command.addOption(new Option("--role <role>", "Project template scope").choices(["production", "preview"]));
47
+ addGlobalFlags(command);
48
+ command.action(async (options) => {
49
+ const roleName = options.role;
50
+ await runCommand(runtime, "project.env.list", options, (context) => runEnvList(context, { roleName }), {
51
+ renderHuman: (context, descriptor, result) => renderEnvList(context, descriptor, result),
52
+ renderJson: (result) => serializeEnvList(result)
53
+ });
54
+ });
55
+ return command;
56
+ }
57
+ function createEnvRmCommand(runtime) {
58
+ const command = attachCommandDescriptor(configureRuntimeCommand(new Command("rm"), runtime), "project.env.rm");
59
+ command.argument("<key>", "Variable key to remove").addOption(new Option("--role <role>", "Project template scope (production or preview)").choices(["production", "preview"]));
60
+ addGlobalFlags(command);
61
+ command.action(async (key, options) => {
62
+ const roleName = options.role;
63
+ await runCommand(runtime, "project.env.rm", options, (context) => runEnvRm(context, key, { roleName }), {
64
+ renderHuman: (context, descriptor, result) => renderEnvRm(context, descriptor, result),
65
+ renderJson: (result) => serializeEnvRm(result)
66
+ });
67
+ });
68
+ return command;
69
+ }
70
+ //#endregion
71
+ export { createEnvCommand };
@@ -1,16 +1,19 @@
1
1
  import { attachCommandDescriptor } from "../../shell/command-meta.js";
2
- import { addGlobalFlags } from "../../shell/global-flags.js";
2
+ import { addCompactGlobalFlags, addGlobalFlags } from "../../shell/global-flags.js";
3
3
  import { configureRuntimeCommand } from "../../shell/runtime.js";
4
4
  import { runCommand } from "../../shell/command-runner.js";
5
5
  import { runProjectLink, runProjectList, runProjectShow } from "../../controllers/project.js";
6
6
  import { renderProjectLink, renderProjectList, renderProjectShow, serializeProjectList } from "../../presenters/project.js";
7
+ import { createEnvCommand } from "../env.js";
7
8
  import { Command } from "commander";
8
9
  //#region src/commands/project/index.ts
9
10
  function createProjectCommand(runtime) {
10
11
  const project = attachCommandDescriptor(configureRuntimeCommand(new Command("project"), runtime), "project");
12
+ addCompactGlobalFlags(project);
11
13
  project.addCommand(createProjectListCommand(runtime));
12
14
  project.addCommand(createProjectShowCommand(runtime));
13
15
  project.addCommand(createProjectLinkCommand(runtime));
16
+ project.addCommand(createEnvCommand(runtime));
14
17
  return project;
15
18
  }
16
19
  function createProjectListCommand(runtime) {
@@ -0,0 +1,221 @@
1
+ import { readLinkedProjectId } from "../adapters/config.js";
2
+ import { CliError, authRequiredError, usageError } from "../shell/errors.js";
3
+ import { requireComputeAuth } from "../lib/auth/guard.js";
4
+ import { formatScopeLabel, parseKeyValuePositional, resolveEnvScope } from "../lib/app/env-config.js";
5
+ //#region src/controllers/app-env.ts
6
+ function defaultRoleScope() {
7
+ return {
8
+ kind: "role",
9
+ role: "production"
10
+ };
11
+ }
12
+ async function runEnvAdd(context, rawAssignment, flags) {
13
+ const { key, value } = parseKeyValuePositional(rawAssignment, "add");
14
+ const scope = resolveEnvScope(flags, {
15
+ requireExplicit: true,
16
+ command: "add"
17
+ });
18
+ if (!scope) throw usageError(`prisma-cli project env add requires --role`, "Writing without an explicit scope is rejected.", "Pass --role production or --role preview.", [`prisma-cli project env add ${key}=${value} --role production`], "app");
19
+ const { client, projectId } = await requireClientAndProject(context);
20
+ const resolved = resolveScopeToApi(scope);
21
+ if (await findVariableByNaturalKey(client, projectId, key, resolved)) throw new CliError({
22
+ code: "ENV_VARIABLE_ALREADY_EXISTS",
23
+ domain: "app",
24
+ summary: `Variable "${key}" already exists in ${formatScopeLabel(scope)}`,
25
+ why: "A variable with this key already exists in the targeted scope.",
26
+ fix: "Use `prisma-cli project env update` to change an existing variable's value.",
27
+ exitCode: 1,
28
+ nextSteps: [`prisma-cli project env update ${key}=<new-value> --role ${scope.role}`]
29
+ });
30
+ const { data, error, response } = await client.POST("/v1/environment-variables", { body: {
31
+ projectId,
32
+ class: resolved.apiTarget.class,
33
+ key,
34
+ value
35
+ } });
36
+ if (error || !data) throw apiCallError(`Failed to add ${key}`, response, error);
37
+ return {
38
+ command: "project.env.add",
39
+ result: {
40
+ projectId,
41
+ scope: resolved.descriptor,
42
+ variable: toMetadata(data.data, resolved.descriptor)
43
+ },
44
+ warnings: [],
45
+ nextSteps: []
46
+ };
47
+ }
48
+ async function runEnvUpdate(context, rawAssignment, flags) {
49
+ const { key, value } = parseKeyValuePositional(rawAssignment, "update");
50
+ const scope = resolveEnvScope(flags, {
51
+ requireExplicit: true,
52
+ command: "update"
53
+ });
54
+ if (!scope) throw usageError(`prisma-cli project env update requires --role`, "Writing without an explicit scope is rejected.", "Pass --role production or --role preview.", [`prisma-cli project env update ${key}=${value} --role production`], "app");
55
+ const { client, projectId } = await requireClientAndProject(context);
56
+ const resolved = resolveScopeToApi(scope);
57
+ const existing = await findVariableByNaturalKey(client, projectId, key, resolved);
58
+ if (!existing) throw new CliError({
59
+ code: "ENV_VARIABLE_NOT_FOUND",
60
+ domain: "app",
61
+ summary: `Variable "${key}" not found in ${formatScopeLabel(scope)}`,
62
+ why: "No variable with this key exists in the targeted scope.",
63
+ fix: "Use `prisma-cli project env add` to create a new variable.",
64
+ exitCode: 1,
65
+ nextSteps: [`prisma-cli project env add ${key}=<value> --role ${scope.role}`]
66
+ });
67
+ const { data, error, response } = await client.PATCH("/v1/environment-variables/{envVarId}", {
68
+ params: { path: { envVarId: existing.id } },
69
+ body: { value }
70
+ });
71
+ if (error || !data) throw apiCallError(`Failed to update value for ${key}`, response, error);
72
+ return {
73
+ command: "project.env.update",
74
+ result: {
75
+ projectId,
76
+ scope: resolved.descriptor,
77
+ variable: toMetadata(data.data, resolved.descriptor)
78
+ },
79
+ warnings: [],
80
+ nextSteps: []
81
+ };
82
+ }
83
+ async function runEnvList(context, flags) {
84
+ const scope = resolveEnvScope(flags, {
85
+ requireExplicit: false,
86
+ command: "list"
87
+ }) ?? defaultRoleScope();
88
+ const { client, projectId } = await requireClientAndProject(context);
89
+ const resolved = resolveScopeToApi(scope);
90
+ const variables = await listVariables(client, projectId, resolved);
91
+ return {
92
+ command: "project.env.list",
93
+ result: {
94
+ projectId,
95
+ scope: resolved.descriptor,
96
+ variables: variables.map((row) => toMetadata(row, resolved.descriptor))
97
+ },
98
+ warnings: [],
99
+ nextSteps: variables.length === 0 ? [`prisma-cli project env add KEY=value --role ${scope.role}`] : []
100
+ };
101
+ }
102
+ async function runEnvRm(context, key, flags) {
103
+ if (!key) throw usageError("prisma-cli project env rm requires KEY", "No KEY positional argument was supplied.", "Pass the variable name to remove, e.g. STRIPE_KEY.", ["prisma-cli project env rm STRIPE_KEY --role production"], "app");
104
+ const scope = resolveEnvScope(flags, {
105
+ requireExplicit: true,
106
+ command: "rm"
107
+ });
108
+ if (!scope) throw usageError("prisma-cli project env rm requires --role", "Writing without an explicit scope is rejected.", "Pass --role production or --role preview.", [`prisma-cli project env rm ${key} --role production`], "app");
109
+ const { client, projectId } = await requireClientAndProject(context);
110
+ const resolved = resolveScopeToApi(scope);
111
+ const existing = await findVariableByNaturalKey(client, projectId, key, resolved);
112
+ if (!existing) throw new CliError({
113
+ code: "ENV_VARIABLE_NOT_FOUND",
114
+ domain: "app",
115
+ summary: `Variable "${key}" not found in ${formatScopeLabel(scope)}`,
116
+ why: "No variable with this key exists in the targeted scope, so there is nothing to remove.",
117
+ fix: "Run prisma-cli project env list with the same scope to see the available variables.",
118
+ exitCode: 1,
119
+ nextSteps: [`prisma-cli project env list --role ${scope.role}`]
120
+ });
121
+ const { error, response } = await client.DELETE("/v1/environment-variables/{envVarId}", { params: { path: { envVarId: existing.id } } });
122
+ if (error) throw apiCallError(`Failed to remove ${key}`, response, error);
123
+ return {
124
+ command: "project.env.rm",
125
+ result: {
126
+ projectId,
127
+ scope: resolved.descriptor,
128
+ key
129
+ },
130
+ warnings: [],
131
+ nextSteps: []
132
+ };
133
+ }
134
+ async function requireClientAndProject(context) {
135
+ const projectId = await readLinkedProjectId(context.runtime.cwd);
136
+ if (!projectId) throw new CliError({
137
+ code: "PROJECT_NOT_LINKED",
138
+ domain: "project",
139
+ summary: "Project link required",
140
+ why: "prisma-cli project env needs a linked project for the current repo.",
141
+ fix: "Run prisma project link before managing environment variables.",
142
+ exitCode: 1,
143
+ nextSteps: ["prisma project link"]
144
+ });
145
+ const client = await requireComputeAuth(context.runtime.env);
146
+ if (!client) throw authRequiredError(["prisma auth login"]);
147
+ return {
148
+ client,
149
+ projectId
150
+ };
151
+ }
152
+ function resolveScopeToApi(scope) {
153
+ return {
154
+ scope,
155
+ descriptor: {
156
+ kind: "role",
157
+ role: scope.role
158
+ },
159
+ apiTarget: {
160
+ class: scope.role,
161
+ branchId: null
162
+ }
163
+ };
164
+ }
165
+ async function findVariableByNaturalKey(client, projectId, key, resolved) {
166
+ const { data, error, response } = await client.GET("/v1/environment-variables", { params: { query: {
167
+ projectId,
168
+ class: resolved.apiTarget.class,
169
+ key
170
+ } } });
171
+ if (error || !data) throw apiCallError(`Failed to look up ${key}`, response, error);
172
+ return data.data.filter((row) => rowMatchesScope(row, resolved))[0] ?? null;
173
+ }
174
+ async function listVariables(client, projectId, resolved) {
175
+ const collected = [];
176
+ let cursor;
177
+ while (true) {
178
+ const query = {
179
+ projectId,
180
+ class: resolved.apiTarget.class
181
+ };
182
+ if (cursor !== void 0) query.cursor = cursor;
183
+ const result = await client.GET("/v1/environment-variables", { params: { query } });
184
+ if (result.error || !result.data) throw apiCallError(`Failed to list environment variables`, result.response, result.error);
185
+ const page = result.data.data.filter((row) => rowMatchesScope(row, resolved));
186
+ collected.push(...page);
187
+ if (!result.data.pagination.hasMore || !result.data.pagination.nextCursor) break;
188
+ cursor = result.data.pagination.nextCursor;
189
+ }
190
+ return collected;
191
+ }
192
+ function rowMatchesScope(row, resolved) {
193
+ return row.branchId === null && row.class === resolved.apiTarget.class;
194
+ }
195
+ function toMetadata(row, scope) {
196
+ return {
197
+ id: row.id,
198
+ key: row.key,
199
+ scope,
200
+ isManagedBySystem: row.isManagedBySystem,
201
+ updatedAt: row.updatedAt
202
+ };
203
+ }
204
+ function apiCallError(summary, response, error) {
205
+ const status = response?.status ?? 0;
206
+ const apiCode = error?.error?.code;
207
+ const apiMessage = error?.error?.message;
208
+ const apiHint = error?.error?.hint;
209
+ if (status === 401 || status === 403) return authRequiredError(["prisma auth login"]);
210
+ return new CliError({
211
+ code: apiCode ?? "ENV_API_ERROR",
212
+ domain: "app",
213
+ summary,
214
+ why: apiMessage ?? `The Management API returned status ${status || "unknown"}.`,
215
+ fix: apiHint ?? "Re-run with --trace for the underlying API response details.",
216
+ exitCode: 1,
217
+ nextSteps: []
218
+ });
219
+ }
220
+ //#endregion
221
+ export { runEnvAdd, runEnvList, runEnvRm, runEnvUpdate };