@prisma/cli 3.0.0-dev.97.1 → 8.0.0-rc.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.
Files changed (99) hide show
  1. package/README.md +3 -14
  2. package/dist/adapters/local-state.js +14 -3
  3. package/dist/{lib/auth → auth}/client.js +11 -3
  4. package/dist/auth/errors.js +50 -0
  5. package/dist/{lib/auth → auth}/guard.js +7 -6
  6. package/dist/{lib/auth → auth}/login.js +147 -6
  7. package/dist/{lib/auth/auth-ops.js → auth/operations.js} +63 -13
  8. package/dist/auth/recipient.js +42 -0
  9. package/dist/{adapters → auth}/token-storage.js +28 -7
  10. package/dist/auth/workspaces.js +171 -0
  11. package/dist/cli-command.js +14 -0
  12. package/dist/cli-name.js +12 -0
  13. package/dist/cli.js +1 -1
  14. package/dist/cli2.js +25 -8
  15. package/dist/command-arguments.js +12 -0
  16. package/dist/commands/agent/index.js +60 -0
  17. package/dist/commands/app/index.js +11 -6
  18. package/dist/commands/auth/index.js +2 -3
  19. package/dist/commands/bucket/index.js +123 -0
  20. package/dist/commands/build/index.js +29 -0
  21. package/dist/commands/database/index.js +92 -2
  22. package/dist/commands/feedback/index.js +20 -0
  23. package/dist/commands/git/index.js +1 -1
  24. package/dist/commands/init/index.js +33 -0
  25. package/dist/commands/project/index.js +54 -5
  26. package/dist/controllers/agent-setup.js +52 -0
  27. package/dist/controllers/agent.js +228 -0
  28. package/dist/controllers/app-env-api.js +3 -2
  29. package/dist/controllers/app-env-file.js +1 -1
  30. package/dist/controllers/app-env.js +5 -5
  31. package/dist/controllers/app.js +423 -358
  32. package/dist/controllers/auth.js +62 -254
  33. package/dist/controllers/branch.js +5 -16
  34. package/dist/controllers/bucket.js +184 -0
  35. package/dist/controllers/build.js +88 -0
  36. package/dist/controllers/database.js +230 -85
  37. package/dist/controllers/feedback.js +90 -0
  38. package/dist/controllers/init.js +814 -0
  39. package/dist/controllers/project.js +345 -184
  40. package/dist/controllers/select-prompt-port.js +1 -0
  41. package/dist/{shell/errors.js → errors.js} +8 -40
  42. package/dist/lib/agent/cli-command.js +20 -0
  43. package/dist/lib/agent/constants.js +12 -0
  44. package/dist/lib/agent/package-manager.js +99 -0
  45. package/dist/lib/agent/setup-status.js +83 -0
  46. package/dist/lib/app/app-provider.js +168 -95
  47. package/dist/lib/app/branch-database-deploy.js +70 -42
  48. package/dist/lib/app/branch-database.js +39 -20
  49. package/dist/lib/app/build-settings.js +8 -3
  50. package/dist/lib/app/build.js +16 -14
  51. package/dist/lib/app/bun-project.js +1 -1
  52. package/dist/lib/app/compute-config.js +29 -31
  53. package/dist/lib/app/deploy-plan.js +1 -0
  54. package/dist/lib/app/deploy-progress.js +7 -7
  55. package/dist/lib/app/env-config.js +1 -1
  56. package/dist/lib/app/env-file.js +2 -2
  57. package/dist/lib/app/env-vars.js +1 -1
  58. package/dist/lib/app/local-dev.js +1 -1
  59. package/dist/lib/app/production-deploy-gate.js +3 -2
  60. package/dist/lib/bucket/provider.js +139 -0
  61. package/dist/lib/database/provider.js +235 -17
  62. package/dist/lib/diagnostics.js +2 -1
  63. package/dist/lib/feedback.js +15 -0
  64. package/dist/lib/git/local-branch.js +1 -1
  65. package/dist/lib/project/interactive-setup.js +6 -5
  66. package/dist/lib/project/local-pin.js +1 -1
  67. package/dist/lib/project/provider.js +93 -0
  68. package/dist/lib/project/resolution.js +11 -8
  69. package/dist/lib/project/setup.js +9 -6
  70. package/dist/lib/version.js +3 -2
  71. package/dist/lib/workspace-id.js +18 -0
  72. package/dist/payload-B88Qvzxk-CrtTXSOe.js +34 -0
  73. package/dist/presenters/agent.js +74 -0
  74. package/dist/presenters/app.js +33 -7
  75. package/dist/presenters/auth.js +3 -2
  76. package/dist/presenters/bucket.js +174 -0
  77. package/dist/presenters/database.js +193 -5
  78. package/dist/presenters/feedback.js +26 -0
  79. package/dist/presenters/init.js +30 -0
  80. package/dist/presenters/project.js +98 -4
  81. package/dist/shell/cli-command.js +2 -0
  82. package/dist/shell/command-meta.js +303 -4
  83. package/dist/shell/command-runner.js +13 -6
  84. package/dist/shell/help.js +6 -5
  85. package/dist/shell/output.js +65 -3
  86. package/dist/shell/prompt.js +12 -5
  87. package/dist/shell/runtime.js +4 -21
  88. package/dist/state-dir.js +12 -0
  89. package/dist/{shell/update-check.js → update-check.js} +6 -5
  90. package/dist/v8/cli.js +14699 -0
  91. package/dist/v8/sender.js +192 -0
  92. package/package.json +21 -13
  93. package/dist/adapters/mock-api.js +0 -136
  94. package/dist/lib/app/app-interaction.js +0 -5
  95. package/dist/shell/command-arguments.js +0 -6
  96. package/dist/use-cases/auth.js +0 -145
  97. package/dist/use-cases/branch.js +0 -47
  98. package/dist/use-cases/create-cli-gateways.js +0 -68
  99. package/dist/use-cases/project.js +0 -30
@@ -1,8 +1,9 @@
1
- import { access, readFile, readdir, stat } from "node:fs/promises";
2
1
  import path from "node:path";
2
+ import { access, readFile, readdir, stat } from "node:fs/promises";
3
3
  //#region src/lib/app/branch-database.ts
4
4
  const SKIPPED_DIRECTORIES = new Set([
5
5
  ".git",
6
+ ".agents",
6
7
  ".next",
7
8
  ".nuxt",
8
9
  ".output",
@@ -54,14 +55,18 @@ async function inspectBranchDatabaseSignal(cwd, signal) {
54
55
  } : selectedPrismaOrmSchema.schema;
55
56
  return {
56
57
  schema,
57
- unsupportedSchema: schema ? null : unsupportedPrismaNextConfig ? {
58
- kind: "prisma-next",
59
- path: unsupportedPrismaNextConfig.path,
60
- target: unsupportedPrismaNextConfig.target
61
- } : selectedPrismaOrmSchema.unsupportedSchema,
58
+ unsupportedSchema: schema ? null : toUnsupportedSchema(unsupportedPrismaNextConfig) ?? selectedPrismaOrmSchema.unsupportedSchema,
62
59
  databaseUrlReferences: state.databaseUrlReferences
63
60
  };
64
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
+ }
65
70
  function hasBranchDatabaseSignal(signal) {
66
71
  if (signal.unsupportedSchema) return false;
67
72
  return Boolean(signal.schema || signal.databaseUrlReferences.length > 0);
@@ -69,29 +74,42 @@ function hasBranchDatabaseSignal(signal) {
69
74
  async function scanDirectory(cwd, directory, depth, state, signal) {
70
75
  signal.throwIfAborted();
71
76
  if (depth > MAX_SCAN_DEPTH || state.filesVisited >= MAX_SCAN_FILES) return;
72
- let entries;
73
- try {
74
- entries = await readdir(directory, { withFileTypes: true });
75
- } catch (error) {
76
- if (error.code === "ENOENT") return;
77
- throw error;
78
- }
79
- entries.sort((left, right) => left.name.localeCompare(right.name));
77
+ const entries = await readSortedDirectoryEntries(directory);
80
78
  for (const entry of entries) {
81
79
  signal.throwIfAborted();
82
80
  if (state.filesVisited >= MAX_SCAN_FILES) return;
83
81
  const entryPath = path.join(directory, entry.name);
84
- if (entry.isDirectory()) {
85
- if (!SKIPPED_DIRECTORIES.has(entry.name)) await scanDirectory(cwd, entryPath, depth + 1, state, signal);
82
+ if (isScannableDirectory(entry)) {
83
+ await scanDirectory(cwd, entryPath, depth + 1, state, signal);
86
84
  continue;
87
85
  }
88
86
  if (!entry.isFile()) continue;
89
87
  state.filesVisited += 1;
90
- if (entry.name === "schema.prisma") state.schemaCandidates.push(entryPath);
91
- if (isPrismaNextConfigFile(entry.name)) state.prismaNextConfigCandidates.push(entryPath);
88
+ recordSchemaCandidates(entry.name, entryPath, state);
92
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);
93
90
  }
94
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
+ }
95
113
  async function selectPrismaOrmSchema(cwd, candidates, signal) {
96
114
  const sorted = sortByPreferredRelativePath(cwd, candidates, "schema.prisma");
97
115
  for (const schemaPath of sorted) {
@@ -137,7 +155,7 @@ function sortByPreferredRelativePath(cwd, candidates, preferredRootFile) {
137
155
  })).sort((left, right) => {
138
156
  if (left.relative === preferredRootFile) return -1;
139
157
  if (right.relative === preferredRootFile) return 1;
140
- return left.relative.length - right.relative.length || left.relative.localeCompare(right.relative);
158
+ return left.relative.length - right.relative.length || compareCodeUnits(left.relative, right.relative);
141
159
  }).map((candidate) => candidate.absolute);
142
160
  }
143
161
  async function hasMigrationsDirectory(schemaDirectory, signal) {
@@ -174,8 +192,9 @@ async function classifyPrismaNextConfig(configPath, signal) {
174
192
  target: "unknown"
175
193
  };
176
194
  }
195
+ const SCHEMA_PROVIDER = /\bprovider\s*=\s*"([^"]+)"/;
177
196
  async function classifyPrismaOrmSchemaTarget(schemaPath, signal) {
178
- switch ((await readTextFileIfSmall(schemaPath, signal))?.match(/\bprovider\s*=\s*"([^"]+)"/)?.[1] ?? null) {
197
+ switch ((await readTextFileIfSmall(schemaPath, signal))?.match(SCHEMA_PROVIDER)?.[1] ?? null) {
179
198
  case "postgresql": return "postgresql";
180
199
  case "mongodb": return "mongodb";
181
200
  case "mysql": return "mysql";
@@ -1,5 +1,5 @@
1
- import { readFile } from "node:fs/promises";
2
1
  import path from "node:path";
2
+ import { readFile } from "node:fs/promises";
3
3
  import { resolveBuildSettings, resolveConfiguredBuildSettings } from "@prisma/compute-sdk";
4
4
  //#region src/lib/app/build-settings.ts
5
5
  /** Legacy build-settings file: no longer read or written, only detected for migration. */
@@ -26,7 +26,7 @@ async function detectLegacyBuildSettings(options) {
26
26
  let legacy;
27
27
  try {
28
28
  const parsed = JSON.parse(content);
29
- const buildCommand = parsed.buildCommand === null || typeof parsed.buildCommand === "string" ? typeof parsed.buildCommand === "string" ? parsed.buildCommand.trim() || null : null : void 0;
29
+ const buildCommand = normalizeLegacyBuildCommand(parsed.buildCommand);
30
30
  const outputDirectory = typeof parsed.outputDirectory === "string" ? normalizeRelativePath(parsed.outputDirectory) : void 0;
31
31
  if (buildCommand === void 0 || !outputDirectory) return {
32
32
  kind: "invalid",
@@ -80,10 +80,15 @@ async function resolveConfiguredAppBuildSettings(options) {
80
80
  settings
81
81
  };
82
82
  }
83
+ const WINDOWS_DRIVE_PREFIX = /^[A-Za-z]:/;
84
+ function normalizeLegacyBuildCommand(value) {
85
+ if (typeof value === "string") return value.trim() || null;
86
+ if (value === null) return null;
87
+ }
83
88
  function normalizeRelativePath(value) {
84
89
  const raw = value.trim().replace(/\\/g, "/");
85
90
  if (raw.length === 0 || raw.split("/").includes("..")) return;
86
- if (/^[A-Za-z]:/.test(raw)) return;
91
+ if (WINDOWS_DRIVE_PREFIX.test(raw)) return;
87
92
  const normalized = path.posix.normalize(raw);
88
93
  const segments = normalized.split("/");
89
94
  if (path.win32.isAbsolute(value) || path.posix.isAbsolute(normalized) || segments.includes("..")) return;
@@ -1,18 +1,19 @@
1
- import "./build-settings.js";
2
- import "node:fs/promises";
3
1
  import "node:path";
2
+ import "node:fs/promises";
3
+ import { FRAMEWORKS } from "@prisma/compute-sdk/config";
4
4
  import { normalizeArtifactSymlinks, resolveBuildStrategy } from "@prisma/compute-sdk";
5
5
  //#region src/lib/app/build.ts
6
- const APP_BUILD_TYPES = [
7
- "auto",
8
- "bun",
9
- "nextjs",
10
- "nuxt",
11
- "astro",
12
- "nestjs",
13
- "tanstack-start"
14
- ];
15
- const RESOLVED_APP_BUILD_TYPES = APP_BUILD_TYPES.filter((buildType) => buildType !== "auto");
6
+ const RESOLVED_APP_BUILD_TYPES = [...frameworkBuildTypesByLastOccurrence()];
7
+ const APP_BUILD_TYPES = ["auto", ...RESOLVED_APP_BUILD_TYPES];
8
+ const APP_BUILD_TYPE_LABELS = APP_BUILD_TYPES.join(", ");
9
+ function frameworkBuildTypesByLastOccurrence() {
10
+ const buildTypes = /* @__PURE__ */ new Set();
11
+ for (const framework of FRAMEWORKS) {
12
+ if (buildTypes.has(framework.buildType)) buildTypes.delete(framework.buildType);
13
+ buildTypes.add(framework.buildType);
14
+ }
15
+ return buildTypes;
16
+ }
16
17
  var AppBuildStrategy = class {
17
18
  #appPath;
18
19
  #entrypoint;
@@ -68,13 +69,14 @@ async function executeAppBuild(options) {
68
69
  }
69
70
  }
70
71
  async function resolveAppBuildStrategy(options) {
72
+ const buildType = options.buildType === "auto" && options.entrypoint ? "bun" : options.buildType;
71
73
  return resolveBuildStrategy({
72
74
  appPath: options.appPath,
73
- buildType: options.buildType,
75
+ buildType,
74
76
  entrypoint: options.entrypoint,
75
77
  buildSettings: options.buildSettings,
76
78
  signal: options.signal
77
79
  });
78
80
  }
79
81
  //#endregion
80
- export { APP_BUILD_TYPES, AppBuildStrategy, RESOLVED_APP_BUILD_TYPES, executeAppBuild };
82
+ export { APP_BUILD_TYPES, APP_BUILD_TYPE_LABELS, AppBuildStrategy, RESOLVED_APP_BUILD_TYPES, executeAppBuild };
@@ -1,5 +1,5 @@
1
- import { access, readFile } from "node:fs/promises";
2
1
  import path from "node:path";
2
+ import { access, readFile } from "node:fs/promises";
3
3
  //#region src/lib/app/bun-project.ts
4
4
  async function readBunPackageJson(appPath, signal) {
5
5
  const packageJsonPath = path.join(appPath, "package.json");
@@ -1,6 +1,6 @@
1
- import { CliError } from "../../shell/errors.js";
2
- import { COMPUTE_CONFIG_FILENAME, COMPUTE_CONFIG_FILENAME as COMPUTE_CONFIG_FILENAME$1, ComputeConfigTargetRequiredError, computeTargetAppDir, frameworkByKey, inferComputeTargetFromCwd, loadComputeConfig, selectComputeDeployTarget } from "@prisma/compute-sdk/config";
1
+ import { CliError } from "../../errors.js";
3
2
  import path from "node:path";
3
+ import { COMPUTE_CONFIG_FILENAME, frameworkByKey, loadComputeConfig } from "@prisma/compute-sdk/config";
4
4
  import { matchError } from "better-result";
5
5
  //#region src/lib/app/compute-config.ts
6
6
  /**
@@ -18,47 +18,45 @@ function computeFrameworkToBuildType(framework) {
18
18
  function mergeComputeDeployInputs(options) {
19
19
  const { cli, target, configFilename } = options;
20
20
  const configAnnotation = `set by ${configFilename}`;
21
- const framework = cli.framework ? {
22
- value: cli.framework,
23
- annotation: "set by --framework"
24
- } : target?.framework ? {
25
- value: target.framework,
26
- annotation: configAnnotation
27
- } : void 0;
28
- const entrypoint = cli.entrypoint ? {
29
- value: cli.entrypoint,
30
- annotation: "set by --entry"
31
- } : target?.entry ? {
32
- value: target.entry,
33
- annotation: configAnnotation
34
- } : void 0;
35
- const httpPort = cli.httpPort ? {
36
- value: cli.httpPort,
37
- annotation: "set by --http-port"
38
- } : target?.httpPort ? {
39
- value: String(target.httpPort),
40
- annotation: configAnnotation
41
- } : void 0;
21
+ const framework = mergeStringInput(cli.framework, "set by --framework", target?.framework ?? void 0, configAnnotation);
22
+ const entrypoint = mergeStringInput(cli.entrypoint, "set by --entry", target?.entry ?? void 0, configAnnotation);
23
+ const httpPort = mergeStringInput(cli.httpPort, "set by --http-port", target?.httpPort ? String(target.httpPort) : void 0, configAnnotation);
24
+ const region = mergeStringInput(cli.region, "set by --region", target?.region ?? void 0, configAnnotation);
42
25
  const cliEnvInputs = cli.envInputs && cli.envInputs.length > 0 ? cli.envInputs : void 0;
43
26
  const configEnvInputs = target && target.envInputs.length > 0 ? target.envInputs : void 0;
44
27
  const envInputs = cliEnvInputs ?? configEnvInputs;
45
- const configAppName = target?.name ? {
46
- value: target.name,
47
- annotation: configAnnotation
48
- } : target?.key ? {
49
- value: target.key,
50
- annotation: configAnnotation
51
- } : void 0;
28
+ const configAppName = readConfigAppName(target, configAnnotation);
52
29
  return {
53
30
  framework,
54
31
  entrypoint,
55
32
  httpPort,
33
+ region,
56
34
  envInputs,
57
35
  envInputsFromConfig: !cliEnvInputs && configEnvInputs !== void 0,
58
36
  configAppName,
59
37
  appRoot: target?.root ?? void 0
60
38
  };
61
39
  }
40
+ function mergeStringInput(cliValue, cliAnnotation, configValue, configAnnotation) {
41
+ if (cliValue !== void 0) return {
42
+ value: cliValue,
43
+ annotation: cliAnnotation
44
+ };
45
+ if (configValue) return {
46
+ value: configValue,
47
+ annotation: configAnnotation
48
+ };
49
+ }
50
+ function readConfigAppName(target, configAnnotation) {
51
+ if (target?.name) return {
52
+ value: target.name,
53
+ annotation: configAnnotation
54
+ };
55
+ if (target?.key) return {
56
+ value: target.key,
57
+ annotation: configAnnotation
58
+ };
59
+ }
62
60
  /**
63
61
  * Merges CLI inputs for the local `app build` and `app run` commands with a
64
62
  * selected config target. Explicit flags win; `--build-type auto` is the
@@ -144,4 +142,4 @@ function computeConfigErrorToCliError(error, commandName = "deploy") {
144
142
  });
145
143
  }
146
144
  //#endregion
147
- export { COMPUTE_CONFIG_FILENAME$1 as COMPUTE_CONFIG_FILENAME, ComputeConfigTargetRequiredError, computeConfigErrorToCliError, computeFrameworkToBuildType, computeTargetAppDir, inferComputeTargetFromCwd, loadComputeConfig$1 as loadComputeConfig, mergeComputeDeployInputs, mergeComputeLocalInputs, selectComputeDeployTarget };
145
+ export { computeConfigErrorToCliError, computeFrameworkToBuildType, loadComputeConfig$1 as loadComputeConfig, mergeComputeDeployInputs, mergeComputeLocalInputs };
@@ -30,6 +30,7 @@ function perAppInputsForDeployAll(inputs) {
30
30
  ["--framework", inputs.framework],
31
31
  ["--entry", inputs.entrypoint],
32
32
  ["--http-port", inputs.httpPort],
33
+ ["--region", inputs.region],
33
34
  ["--env", inputs.envAssignments?.length ? inputs.envAssignments : void 0],
34
35
  [inputs.appIdEnvVar.name, inputs.appIdEnvVar.value]
35
36
  ].filter(([, value]) => value !== void 0).map(([flag]) => flag);
@@ -6,7 +6,7 @@ function createDeployProgressState() {
6
6
  buildCompleted: false,
7
7
  archiveReady: false,
8
8
  uploadCompleted: false,
9
- versionId: null,
9
+ deploymentId: null,
10
10
  startRequested: false,
11
11
  containerLive: false,
12
12
  deploymentUrl: null,
@@ -39,8 +39,8 @@ function createDeployProgress(output, ui, enabled, state = createDeployProgressS
39
39
  onUploadStart() {
40
40
  write("Uploading...");
41
41
  },
42
- onVersionCreated(versionId) {
43
- state.versionId = versionId;
42
+ onDeploymentCreated(deploymentId) {
43
+ state.deploymentId = deploymentId;
44
44
  },
45
45
  onUploadComplete() {
46
46
  state.uploadCompleted = true;
@@ -69,16 +69,16 @@ function createPromoteProgress(output, enabled) {
69
69
  output.write(`${line}\n`);
70
70
  };
71
71
  return {
72
- onVersionStarting(versionId) {
73
- write(`Starting deployment ${versionId}...`);
72
+ onDeploymentStarting(deploymentId) {
73
+ write(`Starting deployment ${deploymentId}...`);
74
74
  },
75
- onVersionStartRequested() {
75
+ onDeploymentStartRequested() {
76
76
  write("Requesting deployment start...");
77
77
  },
78
78
  onStatusChange(status) {
79
79
  write(`Status: ${status}`);
80
80
  },
81
- onVersionRunning() {
81
+ onDeploymentRunning() {
82
82
  write("Deployment is running.");
83
83
  },
84
84
  onPromoteStart() {
@@ -1,4 +1,4 @@
1
- import { usageError } from "../../shell/errors.js";
1
+ import { usageError } from "../../errors.js";
2
2
  //#region src/lib/app/env-config.ts
3
3
  const VALID_ROLES = new Set(["production", "preview"]);
4
4
  function positionalHint(command) {
@@ -1,7 +1,7 @@
1
- import { usageError } from "../../shell/errors.js";
1
+ import { usageError } from "../../errors.js";
2
2
  import { validateKey } from "./env-config.js";
3
- import { readFile } from "node:fs/promises";
4
3
  import path from "node:path";
4
+ import { readFile } from "node:fs/promises";
5
5
  import { parse } from "dotenv";
6
6
  //#region src/lib/app/env-file.ts
7
7
  const ASSIGNMENT_KEY_PATTERN = /^\s*(?:export\s+)?([^#=\s]+)\s*=/;
@@ -1,4 +1,4 @@
1
- import { usageError } from "../../shell/errors.js";
1
+ import { usageError } from "../../errors.js";
2
2
  import { validateKey } from "./env-config.js";
3
3
  import { readEnvFileAssignments } from "./env-file.js";
4
4
  //#region src/lib/app/env-vars.ts
@@ -1,6 +1,6 @@
1
1
  import { resolveBunEntrypoint } from "./bun-project.js";
2
- import "node:fs/promises";
3
2
  import path from "node:path";
3
+ import "node:fs/promises";
4
4
  import { spawn } from "node:child_process";
5
5
  //#region src/lib/app/local-dev.ts
6
6
  const DEFAULT_LOCAL_DEV_PORT = 3e3;
@@ -1,6 +1,6 @@
1
- import { CliError } from "../../shell/errors.js";
2
- import { confirmPrompt } from "../../shell/prompt.js";
1
+ import { CliError } from "../../errors.js";
3
2
  import { canPrompt } from "../../shell/runtime.js";
3
+ import { confirmPrompt } from "../../shell/prompt.js";
4
4
  //#region src/lib/app/production-deploy-gate.ts
5
5
  async function enforceProductionDeployGate(context, provider, options) {
6
6
  if (options.branchKind !== "production") return { firstProductionDeploy: false };
@@ -25,6 +25,7 @@ async function enforceProductionDeployGate(context, provider, options) {
25
25
  if (!await confirmPrompt({
26
26
  input: context.runtime.stdin,
27
27
  output: context.output.stderr,
28
+ signal: context.runtime.signal,
28
29
  message: "Deploy to production?",
29
30
  initialValue: false
30
31
  })) throw productionDeployCancelledError();
@@ -0,0 +1,139 @@
1
+ import { CliError } from "../../errors.js";
2
+ //#region src/lib/bucket/provider.ts
3
+ function createManagementBucketProvider(client) {
4
+ return {
5
+ async listBuckets(options) {
6
+ const buckets = [];
7
+ let cursor;
8
+ while (true) {
9
+ const result = await client.GET("/v1/buckets", {
10
+ params: { query: {
11
+ projectId: options.projectId,
12
+ branchGitName: options.branchName,
13
+ cursor
14
+ } },
15
+ signal: options.signal
16
+ });
17
+ if (result.error || !result.data) throw bucketApiError("Failed to list buckets", result.response, result.error);
18
+ const data = result.data;
19
+ buckets.push(...data.data);
20
+ if (!data.pagination.hasMore || !data.pagination.nextCursor) break;
21
+ cursor = data.pagination.nextCursor;
22
+ }
23
+ return buckets.map(normalizeBucket);
24
+ },
25
+ async createBucket(options) {
26
+ const result = await client.POST("/v1/buckets", {
27
+ body: {
28
+ projectId: options.projectId,
29
+ ...options.name ? { name: options.name } : {},
30
+ ...options.branchGitName ? { branchGitName: options.branchGitName } : {}
31
+ },
32
+ signal: options.signal
33
+ });
34
+ if (result.error || !result.data) throw bucketApiError("Failed to create bucket", result.response, result.error);
35
+ const data = result.data;
36
+ return normalizeBucket(data.data);
37
+ },
38
+ async deleteBucket(bucketId, options) {
39
+ const result = await client.DELETE("/v1/buckets/{bucketId}", {
40
+ params: { path: { bucketId } },
41
+ signal: options?.signal
42
+ });
43
+ if (result.error) throw bucketApiError("Failed to delete bucket", result.response, result.error);
44
+ },
45
+ async listKeys(bucketId, options) {
46
+ const keys = [];
47
+ let cursor;
48
+ while (true) {
49
+ const result = await client.GET("/v1/buckets/{bucketId}/keys", {
50
+ params: {
51
+ path: { bucketId },
52
+ query: { cursor }
53
+ },
54
+ signal: options?.signal
55
+ });
56
+ if (result.error || !result.data) throw bucketApiError("Failed to list bucket keys", result.response, result.error);
57
+ const data = result.data;
58
+ keys.push(...data.data);
59
+ if (!data.pagination.hasMore || !data.pagination.nextCursor) break;
60
+ cursor = data.pagination.nextCursor;
61
+ }
62
+ return keys.map(normalizeKey);
63
+ },
64
+ async createKey(options) {
65
+ const result = await client.POST("/v1/buckets/{bucketId}/keys", {
66
+ params: { path: { bucketId: options.bucketId } },
67
+ body: {
68
+ role: options.role,
69
+ ...options.name ? { name: options.name } : {}
70
+ },
71
+ signal: options.signal
72
+ });
73
+ if (result.error || !result.data) throw bucketApiError("Failed to create bucket key", result.response, result.error);
74
+ const raw = result.data.data;
75
+ const secretAccessKey = raw.secretAccessKey;
76
+ const accessKeyId = raw.accessKeyId;
77
+ const endpoint = raw.endpoint;
78
+ const bucketName = raw.bucketName;
79
+ if (!secretAccessKey || !accessKeyId || !endpoint || !bucketName) throw new CliError({
80
+ code: "BUCKET_KEY_SECRET_MISSING",
81
+ domain: "bucket",
82
+ summary: "Created bucket key did not return credentials",
83
+ why: "Bucket key credentials are one-time-view secrets, but the Management API did not include them in this create response.",
84
+ fix: "Create another bucket key and store the returned credentials immediately.",
85
+ exitCode: 1,
86
+ nextSteps: [`prisma-cli bucket key create ${options.bucketId}`]
87
+ });
88
+ return {
89
+ key: normalizeKey(raw),
90
+ secretAccessKey,
91
+ accessKeyId,
92
+ endpoint,
93
+ bucketName
94
+ };
95
+ },
96
+ async deleteKey(bucketId, keyId, options) {
97
+ const result = await client.DELETE("/v1/buckets/{bucketId}/keys/{keyId}", {
98
+ params: { path: {
99
+ bucketId,
100
+ keyId
101
+ } },
102
+ signal: options?.signal
103
+ });
104
+ if (result.error) throw bucketApiError("Failed to delete bucket key", result.response, result.error);
105
+ }
106
+ };
107
+ }
108
+ function normalizeBucket(raw) {
109
+ return {
110
+ id: raw.id,
111
+ name: raw.name,
112
+ status: raw.status,
113
+ branchId: raw.branchId,
114
+ createdAt: raw.createdAt
115
+ };
116
+ }
117
+ function normalizeKey(raw) {
118
+ return {
119
+ id: raw.id,
120
+ name: raw.name,
121
+ role: raw.role,
122
+ valueHint: raw.valueHint,
123
+ createdAt: raw.createdAt
124
+ };
125
+ }
126
+ function bucketApiError(summary, response, error) {
127
+ const status = response?.status ?? 0;
128
+ return new CliError({
129
+ code: error?.error?.code ?? "BUCKET_API_ERROR",
130
+ domain: "bucket",
131
+ summary,
132
+ why: error?.error?.message ?? `The Management API returned status ${status || "unknown"}.`,
133
+ fix: error?.error?.hint ?? "Re-run with --trace for the underlying API response details.",
134
+ exitCode: 1,
135
+ nextSteps: []
136
+ });
137
+ }
138
+ //#endregion
139
+ export { createManagementBucketProvider };