neon 3.0.0 → 3.1.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 (207) hide show
  1. package/README.md +70 -5
  2. package/dist/_chunks/auth_selection-DGgq6ifc.js +83 -0
  3. package/dist/_chunks/cmd_pipeline-CUbBO9U_.js +2818 -0
  4. package/dist/_chunks/credentials-MYdHdKah.js +188 -0
  5. package/dist/_chunks/env-NbA61JR3.js +585 -0
  6. package/dist/_chunks/env_services-Tz9G4JeT.js +531 -0
  7. package/dist/_chunks/paths-DMq0Lt7a.js +151 -0
  8. package/dist/_chunks/profiles-Ir29rqns.js +217 -0
  9. package/dist/_chunks/psql-DWH-kc69.js +2169 -0
  10. package/dist/_chunks/rolldown-runtime-D7D4PA-g.js +13 -0
  11. package/dist/_chunks/secure_file-BucZj4yQ.js +39 -0
  12. package/dist/analytics.js +163 -207
  13. package/dist/api.js +815 -758
  14. package/dist/auth.js +121 -141
  15. package/dist/auth_context.js +39 -53
  16. package/dist/cli.js +4 -7
  17. package/dist/commands/api.js +220 -250
  18. package/dist/commands/api_keys.js +251 -314
  19. package/dist/commands/auth.js +283 -328
  20. package/dist/commands/bootstrap.js +372 -437
  21. package/dist/commands/branches.js +304 -455
  22. package/dist/commands/bucket.js +374 -514
  23. package/dist/commands/checkout.js +213 -298
  24. package/dist/commands/config.js +573 -690
  25. package/dist/commands/connection_string.js +137 -165
  26. package/dist/commands/data_api.js +238 -260
  27. package/dist/commands/databases.js +67 -76
  28. package/dist/commands/deploy.js +31 -25
  29. package/dist/commands/dev.js +639 -719
  30. package/dist/commands/diff.js +156 -200
  31. package/dist/commands/env.js +255 -305
  32. package/dist/commands/functions.js +275 -355
  33. package/dist/commands/index.js +70 -65
  34. package/dist/commands/init.js +84 -119
  35. package/dist/commands/inspect.js +55 -55
  36. package/dist/commands/ip_allow.js +88 -120
  37. package/dist/commands/link.js +874 -1019
  38. package/dist/commands/logs.js +291 -0
  39. package/dist/commands/neon_auth.js +725 -933
  40. package/dist/commands/operations.js +34 -25
  41. package/dist/commands/orgs.js +28 -18
  42. package/dist/commands/profile.js +615 -846
  43. package/dist/commands/projects.js +313 -373
  44. package/dist/commands/psql.js +60 -58
  45. package/dist/commands/roles.js +55 -58
  46. package/dist/commands/schema_diff.js +87 -131
  47. package/dist/commands/set_context.js +34 -26
  48. package/dist/commands/snapshots.js +288 -413
  49. package/dist/commands/status.js +41 -37
  50. package/dist/commands/user.js +21 -10
  51. package/dist/commands/vpc_endpoints.js +85 -113
  52. package/dist/config.js +7 -6
  53. package/dist/config_format.js +50 -66
  54. package/dist/config_template.js +128 -157
  55. package/dist/context.js +183 -235
  56. package/dist/current_branch_fast_path.js +40 -49
  57. package/dist/dev/env.js +2 -446
  58. package/dist/dev/functions.js +54 -68
  59. package/dist/dev/inputs.js +46 -58
  60. package/dist/dev/runtime.js +135 -164
  61. package/dist/dev/websocket.js +766 -959
  62. package/dist/env.js +27 -33
  63. package/dist/env_file.js +118 -132
  64. package/dist/env_services.js +2 -51
  65. package/dist/errors.js +57 -68
  66. package/dist/functions_api.js +45 -43
  67. package/dist/help.js +189 -140
  68. package/dist/index.js +182 -257
  69. package/dist/init/agents.js +137 -118
  70. package/dist/init/auth.js +58 -68
  71. package/dist/init/bootstrap.js +325 -396
  72. package/dist/init/build_config.js +4 -2
  73. package/dist/init/detect_agent.js +56 -101
  74. package/dist/init/editors.js +35 -52
  75. package/dist/init/enrich_output.js +51 -66
  76. package/dist/init/extension.js +134 -171
  77. package/dist/init/inspect.js +179 -266
  78. package/dist/init/interactive.js +510 -622
  79. package/dist/init/neonctl.js +117 -168
  80. package/dist/init/orchestrate.js +157 -173
  81. package/dist/init/phases/auth.js +188 -202
  82. package/dist/init/phases/cleanup.js +23 -23
  83. package/dist/init/phases/db.js +251 -277
  84. package/dist/init/phases/getting_started.js +213 -223
  85. package/dist/init/phases/mcp.js +174 -224
  86. package/dist/init/phases/migrations.js +247 -248
  87. package/dist/init/phases/neon_auth.js +114 -133
  88. package/dist/init/phases/setup.js +546 -703
  89. package/dist/init/phases/skills.js +75 -86
  90. package/dist/init/phases/status.js +72 -67
  91. package/dist/init/resolve_context.js +102 -99
  92. package/dist/init/route_command.js +91 -98
  93. package/dist/init/skills.js +174 -218
  94. package/dist/init/vsix.js +77 -99
  95. package/dist/log.js +17 -16
  96. package/dist/neon_services.js +104 -129
  97. package/dist/parameters.gen.js +481 -471
  98. package/dist/pkg.js +17 -19
  99. package/dist/profile_keys.js +44 -47
  100. package/dist/psql/cli.js +44 -47
  101. package/dist/psql/command/cmd_cond.js +231 -406
  102. package/dist/psql/command/cmd_connect.js +557 -764
  103. package/dist/psql/command/cmd_copy.js +728 -984
  104. package/dist/psql/command/cmd_describe.js +1499 -1688
  105. package/dist/psql/command/cmd_format.js +733 -905
  106. package/dist/psql/command/cmd_io.js +2 -2193
  107. package/dist/psql/command/cmd_lo.js +297 -359
  108. package/dist/psql/command/cmd_meta.js +727 -878
  109. package/dist/psql/command/cmd_misc.js +138 -172
  110. package/dist/psql/command/cmd_pipeline.js +2 -1148
  111. package/dist/psql/command/cmd_restrict.js +119 -155
  112. package/dist/psql/command/cmd_show.js +529 -688
  113. package/dist/psql/command/dispatch.js +260 -325
  114. package/dist/psql/command/inputQueue.js +35 -33
  115. package/dist/psql/command/shared.js +49 -63
  116. package/dist/psql/complete/filenames.js +90 -133
  117. package/dist/psql/complete/index.js +59 -97
  118. package/dist/psql/complete/matcher.js +236 -300
  119. package/dist/psql/complete/psqlVars.js +218 -223
  120. package/dist/psql/complete/queries.js +159 -177
  121. package/dist/psql/complete/rules.js +1493 -2299
  122. package/dist/psql/core/common.js +2 -1253
  123. package/dist/psql/core/help.js +456 -546
  124. package/dist/psql/core/mainloop.js +692 -1303
  125. package/dist/psql/core/prompt.js +391 -408
  126. package/dist/psql/core/settings.js +429 -644
  127. package/dist/psql/core/sqlHelp.js +480 -554
  128. package/dist/psql/core/startup.js +2 -846
  129. package/dist/psql/core/syncVars.js +67 -110
  130. package/dist/psql/core/variables.js +156 -278
  131. package/dist/psql/describe/formatters.js +884 -1285
  132. package/dist/psql/describe/processNamePattern.js +173 -260
  133. package/dist/psql/describe/queries.js +1368 -2403
  134. package/dist/psql/describe/versionGate.js +32 -41
  135. package/dist/psql/index.js +2 -2030
  136. package/dist/psql/io/history.js +232 -271
  137. package/dist/psql/io/input.js +103 -108
  138. package/dist/psql/io/lineEditor/buffer.js +238 -319
  139. package/dist/psql/io/lineEditor/complete.js +135 -213
  140. package/dist/psql/io/lineEditor/filename.js +139 -148
  141. package/dist/psql/io/lineEditor/index.js +653 -870
  142. package/dist/psql/io/lineEditor/keymap.js +544 -702
  143. package/dist/psql/io/lineEditor/vt100.js +294 -341
  144. package/dist/psql/io/pgpass.js +158 -187
  145. package/dist/psql/io/pgservice.js +146 -183
  146. package/dist/psql/io/psqlrc.js +328 -403
  147. package/dist/psql/print/aligned.js +1020 -1683
  148. package/dist/psql/print/asciidoc.js +180 -214
  149. package/dist/psql/print/crosstab.js +281 -442
  150. package/dist/psql/print/csv.js +48 -70
  151. package/dist/psql/print/html.js +195 -226
  152. package/dist/psql/print/json.js +75 -88
  153. package/dist/psql/print/latex.js +291 -364
  154. package/dist/psql/print/pager.js +171 -242
  155. package/dist/psql/print/troff.js +194 -226
  156. package/dist/psql/print/unaligned.js +69 -95
  157. package/dist/psql/print/units.js +167 -169
  158. package/dist/psql/scanner/slash.js +428 -483
  159. package/dist/psql/scanner/sql.js +445 -889
  160. package/dist/psql/scanner/stringutils.js +309 -379
  161. package/dist/psql/types/index.js +8 -7
  162. package/dist/psql/types/scanner.js +25 -22
  163. package/dist/psql/wire/connection.js +2042 -2803
  164. package/dist/psql/wire/copy.js +84 -100
  165. package/dist/psql/wire/notify.js +39 -59
  166. package/dist/psql/wire/pipeline.js +305 -518
  167. package/dist/psql/wire/protocol.js +349 -417
  168. package/dist/psql/wire/sasl.js +180 -265
  169. package/dist/psql/wire/tls.js +400 -561
  170. package/dist/storage_api.js +115 -129
  171. package/dist/test_utils/fixtures.js +94 -113
  172. package/dist/test_utils/oauth_server.js +10 -7
  173. package/dist/test_utils/project_dir.js +33 -0
  174. package/dist/utils/ai_gateway_notice.js +131 -162
  175. package/dist/utils/api_enums.js +21 -28
  176. package/dist/utils/auth.js +10 -4
  177. package/dist/utils/branch_notice.js +20 -19
  178. package/dist/utils/branch_picker.js +83 -89
  179. package/dist/utils/cli_name.js +15 -12
  180. package/dist/utils/compute_units.js +20 -27
  181. package/dist/utils/config_diff.js +127 -158
  182. package/dist/utils/enrichers.js +95 -148
  183. package/dist/utils/esbuild.js +130 -189
  184. package/dist/utils/flags.js +35 -47
  185. package/dist/utils/formats.js +8 -15
  186. package/dist/utils/git_diff.js +69 -80
  187. package/dist/utils/inspect_db.js +101 -143
  188. package/dist/utils/inspect_queries.js +179 -142
  189. package/dist/utils/middlewares.js +39 -45
  190. package/dist/utils/openapi.js +87 -99
  191. package/dist/utils/package_manager.js +312 -110
  192. package/dist/utils/point_in_time.js +49 -53
  193. package/dist/utils/psql.js +89 -106
  194. package/dist/utils/service_picker.js +55 -58
  195. package/dist/utils/string.js +5 -5
  196. package/dist/utils/ui.js +38 -55
  197. package/dist/utils/write_sync.js +26 -35
  198. package/dist/utils/zip.js +4 -3
  199. package/dist/writer.js +67 -87
  200. package/package.json +11 -6
  201. package/dist/_shared/auth_selection.js +0 -86
  202. package/dist/_shared/credentials.js +0 -209
  203. package/dist/_shared/env-core/env.js +0 -558
  204. package/dist/_shared/env-core/reuse-secrets.js +0 -223
  205. package/dist/_shared/paths.js +0 -148
  206. package/dist/_shared/profiles.js +0 -276
  207. package/dist/_shared/secure_file.js +0 -43
@@ -1,322 +1,237 @@
1
- import chalk from "chalk";
2
- import prompts from "prompts";
3
- import { isNeonApiError } from "../api.js";
4
- import { applyContext, contextBranch, readContextFile } from "../context.js";
1
+ import { t as __exportAll } from "../_chunks/rolldown-runtime-D7D4PA-g.js";
5
2
  import { isCi } from "../env.js";
6
3
  import { log } from "../log.js";
7
- import { createBranch, pickBranchInteractively, } from "../utils/branch_picker.js";
4
+ import { isNeonApiError } from "../api.js";
5
+ import { applyContext, contextBranch, readContextFile } from "../context.js";
8
6
  import { getCliName } from "../utils/cli_name.js";
9
- import { fillSingleProject } from "../utils/enrichers.js";
10
7
  import { looksLikeBranchId } from "../utils/formats.js";
11
- import { applyPolicyOnCreate, createBranchFromPolicyOnCheckout, } from "./config.js";
8
+ import { fillSingleProject } from "../utils/enrichers.js";
9
+ import { createBranch, pickBranchInteractively } from "../utils/branch_picker.js";
12
10
  import { autoPullEnvAfterPin } from "./env.js";
13
- import { handler as linkHandler } from "./link.js";
14
- // The positional is optional: omitting it in an interactive terminal opens a
15
- // branch picker. In non-interactive contexts a missing branch is an error.
16
- export const command = "checkout [id|name]";
17
- export const describe = "Pin a branch in the local context (.neon) so subsequent commands target it";
18
- export const builder = (argv) => argv
19
- .usage("$0 checkout [id|name] [options]")
20
- .positional("id", {
21
- describe: "Branch name or id to check out. Omit to pick interactively from the list of branches.",
22
- type: "string",
23
- })
24
- .options({
25
- "project-id": {
26
- describe: "Project ID",
27
- type: "string",
28
- },
29
- "env-pull": {
30
- describe: "Pull the branch's Neon env vars (DATABASE_URL, …) into a local .env after " +
31
- "checkout. On by default; use --no-env-pull to skip (e.g. when injecting env at " +
32
- "runtime with `neon-env run` / `neon dev`).",
33
- type: "boolean",
34
- default: true,
35
- },
36
- })
37
- .example([
38
- [
39
- "$0 checkout",
40
- "Pick a branch interactively from the project in the closest .neon file",
41
- ],
42
- [
43
- "$0 checkout main",
44
- 'Pin the branch named "main" in the closest .neon file',
45
- ],
46
- [
47
- "$0 checkout br-cool-snow-12345678 --project-id project-id-123",
48
- "Pin a branch by id for an explicit project",
49
- ],
11
+ import { applyPolicyOnCreate, createBranchFromPolicyOnCheckout } from "./config.js";
12
+ import { handler as handler$1 } from "./link.js";
13
+ import chalk from "chalk";
14
+ import prompts from "prompts";
15
+ //#region src/commands/checkout.ts
16
+ var checkout_exports = /* @__PURE__ */ __exportAll({
17
+ builder: () => builder,
18
+ command: () => command,
19
+ describe: () => describe,
20
+ handler: () => handler
21
+ });
22
+ const command = "checkout [id|name]";
23
+ const describe = "Pin a branch in the local context (.neon) so subsequent commands target it";
24
+ const builder = (argv) => argv.usage("$0 checkout [id|name] [options]").positional("id", {
25
+ describe: "Branch name or id to check out. Omit to pick interactively from the list of branches.",
26
+ type: "string"
27
+ }).options({
28
+ "project-id": {
29
+ describe: "Project ID",
30
+ type: "string"
31
+ },
32
+ "env-pull": {
33
+ describe: "Pull the branch's Neon env vars (DATABASE_URL, …) into a local .env after checkout. On by default; use --no-env-pull to skip (e.g. when injecting env at runtime with `neon-env run` / `neon dev`).",
34
+ type: "boolean",
35
+ default: true
36
+ }
37
+ }).example([
38
+ ["$0 checkout", "Pick a branch interactively from the project in the closest .neon file"],
39
+ ["$0 checkout main", "Pin the branch named \"main\" in the closest .neon file"],
40
+ ["$0 checkout br-cool-snow-12345678 --project-id project-id-123", "Pin a branch by id for an explicit project"]
50
41
  ]);
51
- export const handler = async (props) => {
52
- // Show where the context is pinned *before* we switch it, so the user sees the move
53
- // ("currently on X" → "checked out Y") and can catch a checkout they didn't mean to make.
54
- // Read straight from `.neon` (a name, no API call); silent when nothing is pinned yet.
55
- const previousBranch = contextBranch(readContextFile(props.contextFile));
56
- if (previousBranch) {
57
- log.info("%s Currently on branch %s", chalk.dim("→"), chalk.cyan.bold(previousBranch));
58
- }
59
- // Branch listing is project-scoped, so `projectId` is the only thing
60
- // `checkout` actually needs. Resolve it through the standard chain
61
- // (--project-id flag > .neon file > single-project auto-detect); when
62
- // nothing resolves, fall back to an interactive `neonctl link`.
63
- const projectId = await resolveProjectId(props);
64
- const { branchId, branchName, created, policyApplied, policyFailure } = await resolveBranchId(props, projectId);
65
- const orgId = await resolveOrgId(props, projectId);
66
- // `checkout` is a thin helper over `link`. It fully "heals" the context file:
67
- // it always (re)writes `projectId`, `branch`, and `orgId` (when the project
68
- // has one) so a `.neon` that drifted or was missing fields ends up complete
69
- // and consistent after checkout. The branch is stored as its name when known
70
- // (see `link`'s `branch` field), matching what `link` writes.
71
- applyContext(props.contextFile, {
72
- projectId,
73
- ...(orgId ? { orgId } : {}),
74
- branch: branchName,
75
- });
76
- log.info("Checked out branch %s on project %s%s. Updated %s.", branchId, projectId, orgId ? ` (org ${orgId})` : "", props.contextFile);
77
- // When checkout *created* the branch and a neon.ts exists, the branch was created straight
78
- // from the policy (evaluated as a new branch) so its settings/infra are already applied —
79
- // see `policyApplied`. The fallback below covers the case where the branch was created bare
80
- // (e.g. a policy-driven create wasn't possible); `applyPolicyOnCreate` is a no-op when there
81
- // is no neon.ts on disk. Checking out an existing branch never reconciles it.
82
- const failure = created && !policyApplied
83
- ? await applyPolicyOrDescribeFailure({
84
- projectId,
85
- branchId,
86
- ...(props.apiKey ? { apiKey: props.apiKey } : {}),
87
- ...(props.apiHost ? { apiHost: props.apiHost } : {}),
88
- ...(props.color !== undefined
89
- ? { color: props.color }
90
- : {}),
91
- })
92
- : policyFailure;
93
- // Bundle `env pull` so the branch-first loop is just link + checkout: the branch you
94
- // checked out is immediately usable for local dev. `--no-env-pull` opts out.
95
- await autoPullEnvAfterPin({
96
- ...props,
97
- projectId,
98
- branch: branchId,
99
- envPull: props.envPull,
100
- });
101
- // A policy that didn't fully apply is reported last, after the pin and the env pull, so the
102
- // created branch is left in a consistent, usable, re-runnable state: the failure is what
103
- // needs fixing, not the checkout. Still a non-zero exit — the branch does not match the
104
- // policy, and `checkout` will not reconcile it on a second run.
105
- if (failure) {
106
- throw new Error([
107
- `Branch ${branchName} (${branchId}) was created and checked out, but applying neon.ts to it failed: ${failure}`,
108
- `The branch is usable but does not match the policy, and \`${getCliName()} checkout\` never reconciles a branch that already exists.`,
109
- `Fix the cause above, then run \`${getCliName()} deploy --update-existing\` to apply the policy to it — or, if your policy only configures new branches (keyed on \`!branch.exists\`), delete the branch and check it out again: \`${getCliName()} branches delete ${branchName}\` then \`${getCliName()} checkout ${branchName}\`.`,
110
- ].join("\n"));
111
- }
42
+ const handler = async (props) => {
43
+ const previousBranch = contextBranch(readContextFile(props.contextFile));
44
+ if (previousBranch) log.info("%s Currently on branch %s", chalk.dim("→"), chalk.cyan.bold(previousBranch));
45
+ const projectId = await resolveProjectId(props);
46
+ const { branchId, branchName, created, policyApplied, policyFailure } = await resolveBranchId(props, projectId);
47
+ const orgId = await resolveOrgId(props, projectId);
48
+ applyContext(props.contextFile, {
49
+ projectId,
50
+ ...orgId ? { orgId } : {},
51
+ branch: branchName
52
+ });
53
+ log.info("Checked out branch %s on project %s%s. Updated %s.", branchId, projectId, orgId ? ` (org ${orgId})` : "", props.contextFile);
54
+ const failure = created && !policyApplied ? await applyPolicyOrDescribeFailure({
55
+ projectId,
56
+ branchId,
57
+ ...props.apiKey ? { apiKey: props.apiKey } : {},
58
+ ...props.apiHost ? { apiHost: props.apiHost } : {},
59
+ ...props.color !== void 0 ? { color: props.color } : {}
60
+ }) : policyFailure;
61
+ await autoPullEnvAfterPin({
62
+ ...props,
63
+ projectId,
64
+ branch: branchId,
65
+ envPull: props.envPull
66
+ });
67
+ if (failure) throw new Error([
68
+ `Branch ${branchName} (${branchId}) was created and checked out, but applying neon.ts to it failed: ${failure}`,
69
+ `The branch is usable but does not match the policy, and \`${getCliName()} checkout\` never reconciles a branch that already exists.`,
70
+ `Fix the cause above, then run \`${getCliName()} deploy --update-existing\` to apply the policy to it — or, if your policy only configures new branches (keyed on \`!branch.exists\`), delete the branch and check it out again: \`${getCliName()} branches delete ${branchName}\` then \`${getCliName()} checkout ${branchName}\`.`
71
+ ].join("\n"));
112
72
  };
113
73
  /**
114
- * Apply the policy to a branch `checkout` just created bare, returning the failure message
115
- * instead of throwing it. The branch and the context pin already stand at this point, so a
116
- * failed apply must not abort the rest of the checkout — the handler reports it at the end.
117
- */
74
+ * Apply the policy to a branch `checkout` just created bare, returning the failure message
75
+ * instead of throwing it. The branch and the context pin already stand at this point, so a
76
+ * failed apply must not abort the rest of the checkout — the handler reports it at the end.
77
+ */
118
78
  const applyPolicyOrDescribeFailure = async (props) => {
119
- try {
120
- await applyPolicyOnCreate(props);
121
- return undefined;
122
- }
123
- catch (err) {
124
- return err instanceof Error ? err.message : String(err);
125
- }
79
+ try {
80
+ await applyPolicyOnCreate(props);
81
+ return;
82
+ } catch (err) {
83
+ return err instanceof Error ? err.message : String(err);
84
+ }
126
85
  };
127
86
  const resolveBranchId = async (props, projectId) => {
128
- const branches = (await props.apiClient.listProjectBranches({ projectId }))
129
- .data.branches;
130
- if (!props.id) {
131
- const picked = await pickBranchInteractively(branches, {
132
- message: "Which branch would you like to check out?",
133
- nonInteractiveMessage: `No branch specified. Pass a branch name or id (e.g. \`${getCliName()} checkout main\`), ` +
134
- "or run interactively to pick one from a list.",
135
- });
136
- if (picked.kind === "existing") {
137
- const existing = branches.find((b) => b.id === picked.branchId);
138
- return {
139
- branchId: picked.branchId,
140
- branchName: existing?.name ?? picked.branchId,
141
- created: false,
142
- policyApplied: false,
143
- };
144
- }
145
- // The user chose "create a new branch" from the picker.
146
- return createCheckoutBranch(props, projectId, picked.name, branches);
147
- }
148
- const ref = props.id;
149
- // A `br-…` value is an id; match strictly by id and never offer to create.
150
- if (looksLikeBranchId(ref)) {
151
- const byId = branches.find((b) => b.id === ref);
152
- if (byId) {
153
- return {
154
- branchId: byId.id,
155
- branchName: byId.name ?? byId.id,
156
- created: false,
157
- policyApplied: false,
158
- };
159
- }
160
- throw new Error(notFoundMessage(ref, branches));
161
- }
162
- const byName = branches.find((b) => b.name === ref);
163
- if (byName) {
164
- return {
165
- branchId: byName.id,
166
- branchName: byName.name ?? byName.id,
167
- created: false,
168
- policyApplied: false,
169
- };
170
- }
171
- // Name not found: offer to create it interactively, mirroring `branch create`.
172
- if (isCi() || !process.stdout.isTTY) {
173
- throw new Error(notFoundMessage(ref, branches));
174
- }
175
- log.error(notFoundMessage(ref, branches));
176
- const { create } = await prompts({
177
- type: "confirm",
178
- name: "create",
179
- message: `Branch "${ref}" does not exist. Create it now?`,
180
- initial: true,
181
- });
182
- if (!create) {
183
- throw new Error(`Aborted: branch "${ref}" was not found and not created.`);
184
- }
185
- return createCheckoutBranch(props, projectId, ref, branches);
87
+ const branches = (await props.apiClient.listProjectBranches({ projectId })).data.branches;
88
+ if (!props.id) {
89
+ const picked = await pickBranchInteractively(branches, {
90
+ message: "Which branch would you like to check out?",
91
+ nonInteractiveMessage: `No branch specified. Pass a branch name or id (e.g. \`${getCliName()} checkout main\`), or run interactively to pick one from a list.`
92
+ });
93
+ if (picked.kind === "existing") {
94
+ const existing = branches.find((b) => b.id === picked.branchId);
95
+ return {
96
+ branchId: picked.branchId,
97
+ branchName: existing?.name ?? picked.branchId,
98
+ created: false,
99
+ policyApplied: false
100
+ };
101
+ }
102
+ return createCheckoutBranch(props, projectId, picked.name, branches);
103
+ }
104
+ const ref = props.id;
105
+ if (looksLikeBranchId(ref)) {
106
+ const byId = branches.find((b) => b.id === ref);
107
+ if (byId) return {
108
+ branchId: byId.id,
109
+ branchName: byId.name ?? byId.id,
110
+ created: false,
111
+ policyApplied: false
112
+ };
113
+ throw new Error(notFoundMessage(ref, branches));
114
+ }
115
+ const byName = branches.find((b) => b.name === ref);
116
+ if (byName) return {
117
+ branchId: byName.id,
118
+ branchName: byName.name ?? byName.id,
119
+ created: false,
120
+ policyApplied: false
121
+ };
122
+ if (isCi() || !process.stdout.isTTY) throw new Error(notFoundMessage(ref, branches));
123
+ log.error(notFoundMessage(ref, branches));
124
+ const { create } = await prompts({
125
+ type: "confirm",
126
+ name: "create",
127
+ message: `Branch "${ref}" does not exist. Create it now?`,
128
+ initial: true
129
+ });
130
+ if (!create) throw new Error(`Aborted: branch "${ref}" was not found and not created.`);
131
+ return createCheckoutBranch(props, projectId, ref, branches);
186
132
  };
187
133
  /**
188
- * Create the branch to check out. When a `neon.ts` exists, route through the policy-driven
189
- * create so the new branch comes up branched from the policy's `parent` and configured with
190
- * its declared TTL / compute / services (evaluated as a *new* branch). Otherwise fall back to
191
- * a bare branch off the default — the handler then applies the policy (a no-op with no
192
- * `neon.ts`).
193
- */
134
+ * Create the branch to check out. When a `neon.ts` exists, route through the policy-driven
135
+ * create so the new branch comes up branched from the policy's `parent` and configured with
136
+ * its declared TTL / compute / services (evaluated as a *new* branch). Otherwise fall back to
137
+ * a bare branch off the default — the handler then applies the policy (a no-op with no
138
+ * `neon.ts`).
139
+ */
194
140
  const createCheckoutBranch = async (props, projectId, name, branches) => {
195
- const fromPolicy = await createBranchFromPolicyOnCheckout({
196
- projectId,
197
- branchName: name,
198
- ...(props.apiKey ? { apiKey: props.apiKey } : {}),
199
- ...(props.apiHost ? { apiHost: props.apiHost } : {}),
200
- ...(props.color !== undefined ? { color: props.color } : {}),
201
- });
202
- if (fromPolicy) {
203
- return {
204
- branchId: fromPolicy.branchId,
205
- branchName: name,
206
- created: true,
207
- policyApplied: true,
208
- ...(fromPolicy.policyFailure
209
- ? { policyFailure: fromPolicy.policyFailure }
210
- : {}),
211
- };
212
- }
213
- return {
214
- branchId: await createBranch(props.apiClient, projectId, name, branches),
215
- branchName: name,
216
- created: true,
217
- policyApplied: false,
218
- };
141
+ const fromPolicy = await createBranchFromPolicyOnCheckout({
142
+ projectId,
143
+ branchName: name,
144
+ ...props.apiKey ? { apiKey: props.apiKey } : {},
145
+ ...props.apiHost ? { apiHost: props.apiHost } : {},
146
+ ...props.color !== void 0 ? { color: props.color } : {}
147
+ });
148
+ if (fromPolicy) return {
149
+ branchId: fromPolicy.branchId,
150
+ branchName: name,
151
+ created: true,
152
+ policyApplied: true,
153
+ ...fromPolicy.policyFailure ? { policyFailure: fromPolicy.policyFailure } : {}
154
+ };
155
+ return {
156
+ branchId: await createBranch(props.apiClient, projectId, name, branches),
157
+ branchName: name,
158
+ created: true,
159
+ policyApplied: false
160
+ };
219
161
  };
220
- const notFoundMessage = (ref, branches) => `Branch ${ref} not found.\nAvailable branches: ${branches
221
- .map((b) => b.name)
222
- .join(", ")}`;
162
+ const notFoundMessage = (ref, branches) => `Branch ${ref} not found.\nAvailable branches: ${branches.map((b) => b.name).join(", ")}`;
223
163
  /**
224
- * Resolve the org id to heal into the context file.
225
- *
226
- * Prefer an org id we already know (from `--org-id`, the `.neon` file, or a
227
- * freshly-run `link`). Otherwise look it up from the project itself so the
228
- * `.neon` file ends up with an accurate `orgId` even when it was previously
229
- * missing. Projects on a personal account have no org; in that case (or if the
230
- * lookup fails for a non-auth reason) we return `undefined` and simply omit the
231
- * field rather than failing the checkout.
232
- */
164
+ * Resolve the org id to heal into the context file.
165
+ *
166
+ * Prefer an org id we already know (from `--org-id`, the `.neon` file, or a
167
+ * freshly-run `link`). Otherwise look it up from the project itself so the
168
+ * `.neon` file ends up with an accurate `orgId` even when it was previously
169
+ * missing. Projects on a personal account have no org; in that case (or if the
170
+ * lookup fails for a non-auth reason) we return `undefined` and simply omit the
171
+ * field rather than failing the checkout.
172
+ */
233
173
  const resolveOrgId = async (props, projectId) => {
234
- if (props.orgId) {
235
- return props.orgId;
236
- }
237
- try {
238
- const { data } = await props.apiClient.getProject(projectId);
239
- return data.project.org_id ?? undefined;
240
- }
241
- catch (err) {
242
- if (isNeonApiError(err) && err.status === 401) {
243
- throw err;
244
- }
245
- log.debug("checkout: could not resolve org id for project %s: %s", projectId, err instanceof Error ? err.message : String(err));
246
- return undefined;
247
- }
174
+ if (props.orgId) return props.orgId;
175
+ try {
176
+ const { data } = await props.apiClient.getProject(projectId);
177
+ return data.project.org_id ?? void 0;
178
+ } catch (err) {
179
+ if (isNeonApiError(err) && err.status === 401) throw err;
180
+ log.debug("checkout: could not resolve org id for project %s: %s", projectId, err instanceof Error ? err.message : String(err));
181
+ return;
182
+ }
248
183
  };
249
184
  /**
250
- * Resolve the project id `checkout` should target.
251
- *
252
- * `props.projectId` is already populated from the `--project-id` flag or the
253
- * closest `.neon` file (via the global `enrichFromContext` middleware). When
254
- * it's still missing we try to auto-detect a single project (same behaviour as
255
- * `branches` / `connection-string`). If that fails we surface a telling error
256
- * and, in an interactive terminal, offer to run `neonctl link` in the current
257
- * folder so the user can pick a project/branch without having to re-run the
258
- * command by hand.
259
- */
185
+ * Resolve the project id `checkout` should target.
186
+ *
187
+ * `props.projectId` is already populated from the `--project-id` flag or the
188
+ * closest `.neon` file (via the global `enrichFromContext` middleware). When
189
+ * it's still missing we try to auto-detect a single project (same behaviour as
190
+ * `branches` / `connection-string`). If that fails we surface a telling error
191
+ * and, in an interactive terminal, offer to run `neonctl link` in the current
192
+ * folder so the user can pick a project/branch without having to re-run the
193
+ * command by hand.
194
+ */
260
195
  const resolveProjectId = async (props) => {
261
- if (props.projectId) {
262
- return props.projectId;
263
- }
264
- const autoDetected = await tryAutoDetectProject(props);
265
- if (autoDetected) {
266
- return autoDetected;
267
- }
268
- const missingProjectMessage = "Could not determine which Neon project to check out a branch from. " +
269
- "Provide one via the --project-id flag " +
270
- `or a .neon file (created by \`${getCliName()} link\` / \`${getCliName()} set-context\`).`;
271
- if (isCi() || !process.stdout.isTTY) {
272
- throw new Error(missingProjectMessage);
273
- }
274
- log.error(missingProjectMessage);
275
- const { runLink } = await prompts({
276
- type: "confirm",
277
- name: "runLink",
278
- message: `Run \`${getCliName()} link\` in the current folder to pick a project now?`,
279
- initial: true,
280
- });
281
- if (!runLink) {
282
- throw new Error("Aborted: no project selected. Re-run with --project-id or link a project first.");
283
- }
284
- await linkHandler({
285
- ...props,
286
- agent: false,
287
- yes: false,
288
- clear: false,
289
- checks: true,
290
- });
291
- const linked = readContextFile(props.contextFile);
292
- if (!linked.projectId) {
293
- throw new Error(`Linking did not produce a project id. Re-run \`${getCliName()} checkout\` once the directory is linked.`);
294
- }
295
- // Carry the freshly-linked org id forward so the merge below keeps it.
296
- if (linked.orgId) {
297
- props.orgId = linked.orgId;
298
- }
299
- return linked.projectId;
196
+ if (props.projectId) return props.projectId;
197
+ const autoDetected = await tryAutoDetectProject(props);
198
+ if (autoDetected) return autoDetected;
199
+ const missingProjectMessage = `Could not determine which Neon project to check out a branch from. Provide one via the --project-id flag or a .neon file (created by \`${getCliName()} link\` / \`${getCliName()} set-context\`).`;
200
+ if (isCi() || !process.stdout.isTTY) throw new Error(missingProjectMessage);
201
+ log.error(missingProjectMessage);
202
+ const { runLink } = await prompts({
203
+ type: "confirm",
204
+ name: "runLink",
205
+ message: `Run \`${getCliName()} link\` in the current folder to pick a project now?`,
206
+ initial: true
207
+ });
208
+ if (!runLink) throw new Error("Aborted: no project selected. Re-run with --project-id or link a project first.");
209
+ await handler$1({
210
+ ...props,
211
+ agent: false,
212
+ yes: false,
213
+ clear: false,
214
+ checks: true
215
+ });
216
+ const linked = readContextFile(props.contextFile);
217
+ if (!linked.projectId) throw new Error(`Linking did not produce a project id. Re-run \`${getCliName()} checkout\` once the directory is linked.`);
218
+ if (linked.orgId) props.orgId = linked.orgId;
219
+ return linked.projectId;
300
220
  };
301
221
  /**
302
- * Best-effort single-project auto-detection. Returns the project id when the
303
- * API key maps to exactly one project, or `undefined` when the project can't be
304
- * determined unambiguously (zero or multiple projects) so the caller can fall
305
- * back to the interactive `link` flow.
306
- */
222
+ * Best-effort single-project auto-detection. Returns the project id when the
223
+ * API key maps to exactly one project, or `undefined` when the project can't be
224
+ * determined unambiguously (zero or multiple projects) so the caller can fall
225
+ * back to the interactive `link` flow.
226
+ */
307
227
  const tryAutoDetectProject = async (props) => {
308
- try {
309
- const filled = await fillSingleProject(props);
310
- return filled.projectId;
311
- }
312
- catch (err) {
313
- // `fillSingleProject` throws on "No projects found" / "Multiple projects
314
- // found" — both mean we can't pick a project automatically. Network/auth
315
- // errors are real and should surface to the user.
316
- if (isNeonApiError(err)) {
317
- throw err;
318
- }
319
- log.debug("checkout: could not auto-detect a single project: %s", err instanceof Error ? err.message : String(err));
320
- return undefined;
321
- }
228
+ try {
229
+ return (await fillSingleProject(props)).projectId;
230
+ } catch (err) {
231
+ if (isNeonApiError(err)) throw err;
232
+ log.debug("checkout: could not auto-detect a single project: %s", err instanceof Error ? err.message : String(err));
233
+ return;
234
+ }
322
235
  };
236
+ //#endregion
237
+ export { builder, command, describe, handler, checkout_exports as t };