neon 3.0.0 → 3.1.0

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 (198) hide show
  1. package/README.md +54 -0
  2. package/dist/_shared/auth_selection.js +76 -79
  3. package/dist/_shared/credentials.js +166 -187
  4. package/dist/_shared/env-core/env.js +354 -517
  5. package/dist/_shared/env-core/reuse-secrets.js +159 -203
  6. package/dist/_shared/paths.js +129 -126
  7. package/dist/_shared/profiles.js +192 -242
  8. package/dist/_shared/secure_file.js +36 -38
  9. package/dist/_virtual/_rolldown/runtime.js +13 -0
  10. package/dist/analytics.js +163 -207
  11. package/dist/api.js +815 -758
  12. package/dist/auth.js +121 -141
  13. package/dist/auth_context.js +39 -53
  14. package/dist/cli.js +4 -7
  15. package/dist/commands/api.js +220 -250
  16. package/dist/commands/api_keys.js +251 -314
  17. package/dist/commands/auth.js +283 -328
  18. package/dist/commands/bootstrap.js +372 -437
  19. package/dist/commands/branches.js +304 -455
  20. package/dist/commands/bucket.js +374 -514
  21. package/dist/commands/checkout.js +213 -298
  22. package/dist/commands/config.js +573 -690
  23. package/dist/commands/connection_string.js +137 -165
  24. package/dist/commands/data_api.js +238 -260
  25. package/dist/commands/databases.js +67 -76
  26. package/dist/commands/deploy.js +31 -25
  27. package/dist/commands/dev.js +639 -719
  28. package/dist/commands/diff.js +156 -200
  29. package/dist/commands/env.js +243 -303
  30. package/dist/commands/functions.js +275 -355
  31. package/dist/commands/index.js +70 -65
  32. package/dist/commands/init.js +84 -119
  33. package/dist/commands/inspect.js +55 -55
  34. package/dist/commands/ip_allow.js +88 -120
  35. package/dist/commands/link.js +874 -1019
  36. package/dist/commands/logs.js +291 -0
  37. package/dist/commands/neon_auth.js +725 -933
  38. package/dist/commands/operations.js +34 -25
  39. package/dist/commands/orgs.js +28 -18
  40. package/dist/commands/profile.js +614 -845
  41. package/dist/commands/projects.js +313 -373
  42. package/dist/commands/psql.js +60 -58
  43. package/dist/commands/roles.js +55 -58
  44. package/dist/commands/schema_diff.js +87 -131
  45. package/dist/commands/set_context.js +34 -26
  46. package/dist/commands/snapshots.js +288 -413
  47. package/dist/commands/status.js +41 -37
  48. package/dist/commands/user.js +21 -10
  49. package/dist/commands/vpc_endpoints.js +85 -113
  50. package/dist/config.js +7 -6
  51. package/dist/config_format.js +50 -66
  52. package/dist/config_template.js +128 -157
  53. package/dist/context.js +183 -235
  54. package/dist/current_branch_fast_path.js +40 -49
  55. package/dist/dev/env.js +313 -394
  56. package/dist/dev/functions.js +54 -68
  57. package/dist/dev/inputs.js +46 -58
  58. package/dist/dev/runtime.js +135 -164
  59. package/dist/dev/websocket.js +766 -959
  60. package/dist/env.js +27 -33
  61. package/dist/env_file.js +118 -132
  62. package/dist/env_services.js +36 -38
  63. package/dist/errors.js +57 -68
  64. package/dist/functions_api.js +45 -43
  65. package/dist/help.js +189 -140
  66. package/dist/index.js +182 -257
  67. package/dist/init/agents.js +137 -118
  68. package/dist/init/auth.js +58 -68
  69. package/dist/init/bootstrap.js +325 -396
  70. package/dist/init/build_config.js +4 -2
  71. package/dist/init/detect_agent.js +56 -101
  72. package/dist/init/editors.js +35 -52
  73. package/dist/init/enrich_output.js +51 -66
  74. package/dist/init/extension.js +134 -171
  75. package/dist/init/inspect.js +179 -266
  76. package/dist/init/interactive.js +510 -622
  77. package/dist/init/neonctl.js +117 -168
  78. package/dist/init/orchestrate.js +157 -173
  79. package/dist/init/phases/auth.js +188 -202
  80. package/dist/init/phases/cleanup.js +23 -23
  81. package/dist/init/phases/db.js +251 -277
  82. package/dist/init/phases/getting_started.js +213 -223
  83. package/dist/init/phases/mcp.js +174 -224
  84. package/dist/init/phases/migrations.js +247 -248
  85. package/dist/init/phases/neon_auth.js +114 -133
  86. package/dist/init/phases/setup.js +546 -703
  87. package/dist/init/phases/skills.js +75 -86
  88. package/dist/init/phases/status.js +72 -67
  89. package/dist/init/resolve_context.js +102 -99
  90. package/dist/init/route_command.js +91 -98
  91. package/dist/init/skills.js +174 -218
  92. package/dist/init/vsix.js +77 -99
  93. package/dist/log.js +17 -16
  94. package/dist/neon_services.js +104 -129
  95. package/dist/parameters.gen.js +481 -471
  96. package/dist/pkg.js +17 -19
  97. package/dist/profile_keys.js +44 -47
  98. package/dist/psql/cli.js +44 -47
  99. package/dist/psql/command/cmd_cond.js +231 -406
  100. package/dist/psql/command/cmd_connect.js +557 -764
  101. package/dist/psql/command/cmd_copy.js +727 -983
  102. package/dist/psql/command/cmd_describe.js +1499 -1688
  103. package/dist/psql/command/cmd_format.js +733 -905
  104. package/dist/psql/command/cmd_io.js +1293 -2082
  105. package/dist/psql/command/cmd_lo.js +297 -359
  106. package/dist/psql/command/cmd_meta.js +727 -878
  107. package/dist/psql/command/cmd_misc.js +138 -172
  108. package/dist/psql/command/cmd_pipeline.js +547 -1099
  109. package/dist/psql/command/cmd_restrict.js +119 -155
  110. package/dist/psql/command/cmd_show.js +529 -688
  111. package/dist/psql/command/dispatch.js +261 -325
  112. package/dist/psql/command/inputQueue.js +35 -33
  113. package/dist/psql/command/shared.js +49 -63
  114. package/dist/psql/complete/filenames.js +90 -133
  115. package/dist/psql/complete/index.js +59 -97
  116. package/dist/psql/complete/matcher.js +236 -300
  117. package/dist/psql/complete/psqlVars.js +218 -223
  118. package/dist/psql/complete/queries.js +159 -177
  119. package/dist/psql/complete/rules.js +1493 -2299
  120. package/dist/psql/core/common.js +762 -1180
  121. package/dist/psql/core/help.js +456 -546
  122. package/dist/psql/core/mainloop.js +692 -1302
  123. package/dist/psql/core/prompt.js +391 -408
  124. package/dist/psql/core/settings.js +429 -644
  125. package/dist/psql/core/sqlHelp.js +480 -554
  126. package/dist/psql/core/startup.js +626 -815
  127. package/dist/psql/core/syncVars.js +67 -110
  128. package/dist/psql/core/variables.js +156 -278
  129. package/dist/psql/describe/formatters.js +884 -1285
  130. package/dist/psql/describe/processNamePattern.js +173 -260
  131. package/dist/psql/describe/queries.js +1368 -2403
  132. package/dist/psql/describe/versionGate.js +32 -41
  133. package/dist/psql/index.js +1414 -1927
  134. package/dist/psql/io/history.js +232 -271
  135. package/dist/psql/io/input.js +103 -108
  136. package/dist/psql/io/lineEditor/buffer.js +238 -319
  137. package/dist/psql/io/lineEditor/complete.js +135 -213
  138. package/dist/psql/io/lineEditor/filename.js +139 -148
  139. package/dist/psql/io/lineEditor/index.js +653 -870
  140. package/dist/psql/io/lineEditor/keymap.js +544 -702
  141. package/dist/psql/io/lineEditor/vt100.js +294 -341
  142. package/dist/psql/io/pgpass.js +158 -187
  143. package/dist/psql/io/pgservice.js +146 -183
  144. package/dist/psql/io/psqlrc.js +328 -403
  145. package/dist/psql/print/aligned.js +1020 -1683
  146. package/dist/psql/print/asciidoc.js +180 -214
  147. package/dist/psql/print/crosstab.js +281 -442
  148. package/dist/psql/print/csv.js +48 -70
  149. package/dist/psql/print/html.js +195 -226
  150. package/dist/psql/print/json.js +75 -88
  151. package/dist/psql/print/latex.js +291 -364
  152. package/dist/psql/print/pager.js +171 -242
  153. package/dist/psql/print/troff.js +194 -226
  154. package/dist/psql/print/unaligned.js +69 -95
  155. package/dist/psql/print/units.js +167 -169
  156. package/dist/psql/scanner/slash.js +428 -483
  157. package/dist/psql/scanner/sql.js +445 -889
  158. package/dist/psql/scanner/stringutils.js +309 -379
  159. package/dist/psql/types/index.js +2 -7
  160. package/dist/psql/types/scanner.js +25 -22
  161. package/dist/psql/wire/connection.js +2042 -2803
  162. package/dist/psql/wire/copy.js +84 -100
  163. package/dist/psql/wire/notify.js +39 -59
  164. package/dist/psql/wire/pipeline.js +305 -518
  165. package/dist/psql/wire/protocol.js +349 -417
  166. package/dist/psql/wire/sasl.js +180 -265
  167. package/dist/psql/wire/tls.js +400 -561
  168. package/dist/storage_api.js +115 -129
  169. package/dist/test_utils/fixtures.js +94 -113
  170. package/dist/test_utils/oauth_server.js +10 -7
  171. package/dist/test_utils/project_dir.js +33 -0
  172. package/dist/utils/ai_gateway_notice.js +131 -162
  173. package/dist/utils/api_enums.js +21 -28
  174. package/dist/utils/auth.js +10 -4
  175. package/dist/utils/branch_notice.js +20 -19
  176. package/dist/utils/branch_picker.js +83 -89
  177. package/dist/utils/cli_name.js +15 -12
  178. package/dist/utils/compute_units.js +20 -27
  179. package/dist/utils/config_diff.js +127 -158
  180. package/dist/utils/enrichers.js +95 -148
  181. package/dist/utils/esbuild.js +130 -189
  182. package/dist/utils/flags.js +35 -47
  183. package/dist/utils/formats.js +8 -15
  184. package/dist/utils/git_diff.js +69 -80
  185. package/dist/utils/inspect_db.js +101 -143
  186. package/dist/utils/inspect_queries.js +179 -142
  187. package/dist/utils/middlewares.js +37 -44
  188. package/dist/utils/openapi.js +87 -99
  189. package/dist/utils/package_manager.js +312 -110
  190. package/dist/utils/point_in_time.js +49 -53
  191. package/dist/utils/psql.js +89 -106
  192. package/dist/utils/service_picker.js +55 -58
  193. package/dist/utils/string.js +5 -5
  194. package/dist/utils/ui.js +38 -55
  195. package/dist/utils/write_sync.js +26 -35
  196. package/dist/utils/zip.js +4 -3
  197. package/dist/writer.js +67 -87
  198. package/package.json +7 -5
@@ -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 { __exportAll } from "../_virtual/_rolldown/runtime.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, checkout_exports, command, describe, handler };