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,180 +1,149 @@
1
1
  import { log } from "../log.js";
2
+ //#region src/utils/ai_gateway_notice.ts
2
3
  /**
3
- * Friendly guidance shown when a branch enables the AI Gateway (`preview.aiGateway`).
4
- *
5
- * The gateway is credential-gated, not provisioned: enabling it always mints a working
6
- * branch credential, so `apply` / `checkout` / `env pull` succeed regardless of plan. The
7
- * two things that *do* gate the gateway happen at serving time and are invisible in the
8
- * provisioning result, so we surface them as a courtesy notice instead:
9
- *
10
- * - **Free plan** — credentials provision, but the gateway does not *serve* model requests.
11
- * The account needs to upgrade to a paid plan.
12
- * - **Reduced model set** — on a paid plan, an account still ramping up on the beta gets a
13
- * trimmed model catalog (flagship models are listed but `enabled: false` on `/v1/models`).
14
- * They can request access to more models.
15
- *
16
- * This is deliberately phrased for the user: it never mentions account "verification" or any
17
- * other internal gating mechanism.
18
- */
4
+ * Friendly guidance shown when a branch enables the AI Gateway (`preview.aiGateway`).
5
+ *
6
+ * The gateway is credential-gated, not provisioned: enabling it always mints a working
7
+ * branch credential, so `apply` / `checkout` / `env pull` succeed regardless of plan. The
8
+ * two things that *do* gate the gateway happen at serving time and are invisible in the
9
+ * provisioning result, so we surface them as a courtesy notice instead:
10
+ *
11
+ * - **Free plan** — credentials provision, but the gateway does not *serve* model requests.
12
+ * The account needs to upgrade to a paid plan.
13
+ * - **Reduced model set** — on a paid plan, an account still ramping up on the beta gets a
14
+ * trimmed model catalog (flagship models are listed but `enabled: false` on `/v1/models`).
15
+ * They can request access to more models.
16
+ *
17
+ * This is deliberately phrased for the user: it never mentions account "verification" or any
18
+ * other internal gating mechanism.
19
+ */
19
20
  /**
20
- * `BillingSubscriptionType` values that mean the account is on a Free plan (the gateway
21
- * won't serve requests). Everything else — `launch`, `scale`, `business`, the `*_v3`
22
- * variants, marketplace plans — is treated as paid.
23
- */
24
- const FREE_SUBSCRIPTION_TYPES = new Set(["free_v2", "free_v3"]);
21
+ * `BillingSubscriptionType` values that mean the account is on a Free plan (the gateway
22
+ * won't serve requests). Everything else — `launch`, `scale`, `business`, the `*_v3`
23
+ * variants, marketplace plans — is treated as paid.
24
+ */
25
+ const FREE_SUBSCRIPTION_TYPES = /* @__PURE__ */ new Set(["free_v2", "free_v3"]);
25
26
  /**
26
- * The Neon Console billing page to upgrade a Free-plan account so the gateway can serve. It's
27
- * org-scoped when the project belongs to an org; projects on a personal account (which is
28
- * where Free plans usually live) have no org id, so we fall back to the account-level page.
29
- */
30
- export const aiGatewayUpgradeUrl = (orgId) => orgId
31
- ? `https://console.neon.tech/app/${orgId}/billing`
32
- : "https://console.neon.tech/app/billing";
27
+ * The Neon Console billing page to upgrade a Free-plan account so the gateway can serve. It's
28
+ * org-scoped when the project belongs to an org; projects on a personal account (which is
29
+ * where Free plans usually live) have no org id, so we fall back to the account-level page.
30
+ */
31
+ const aiGatewayUpgradeUrl = (orgId) => orgId ? `https://console.neon.tech/app/${orgId}/billing` : "https://console.neon.tech/app/billing";
33
32
  /**
34
- * The branch's AI Gateway page in the Neon Console — where a paid user with a reduced model
35
- * set requests access to more models. It's branch-scoped, so it's built from the linked
36
- * project and branch ids.
37
- */
38
- export const aiGatewayModelsUrl = (projectId, branchId) => `https://console.neon.tech/app/projects/${projectId}/branches/${branchId}/ai-gateway`;
39
- export const isFreePlan = (subscriptionType) => subscriptionType !== undefined &&
40
- FREE_SUBSCRIPTION_TYPES.has(subscriptionType);
33
+ * The branch's AI Gateway page in the Neon Console — where a paid user with a reduced model
34
+ * set requests access to more models. It's branch-scoped, so it's built from the linked
35
+ * project and branch ids.
36
+ */
37
+ const aiGatewayModelsUrl = (projectId, branchId) => `https://console.neon.tech/app/projects/${projectId}/branches/${branchId}/ai-gateway`;
38
+ const isFreePlan = (subscriptionType) => subscriptionType !== void 0 && FREE_SUBSCRIPTION_TYPES.has(subscriptionType);
41
39
  /**
42
- * Whether the model catalog includes at least one flagship model enabled. Flagship models
43
- * (Anthropic Opus, OpenAI Codex / `*-pro`) are the first to be held back for an account still
44
- * ramping up on the beta, so their total absence from a non-empty enabled set is the signal
45
- * that the account has a reduced model set. Matched by id substring so it survives model
46
- * version bumps (e.g. `claude-opus-4-8`).
47
- */
48
- export const hasFlagshipModels = (modelIds) => modelIds.some((id) => id.includes("opus") || id.includes("codex") || id.endsWith("-pro"));
40
+ * Whether the model catalog includes at least one flagship model enabled. Flagship models
41
+ * (Anthropic Opus, OpenAI Codex / `*-pro`) are the first to be held back for an account still
42
+ * ramping up on the beta, so their total absence from a non-empty enabled set is the signal
43
+ * that the account has a reduced model set. Matched by id substring so it survives model
44
+ * version bumps (e.g. `claude-opus-4-8`).
45
+ */
46
+ const hasFlagshipModels = (modelIds) => modelIds.some((id) => id.includes("opus") || id.includes("codex") || id.endsWith("-pro"));
49
47
  /**
50
- * The message shown when a `neon.ts` that enables the AI Gateway is applied on a Free plan.
51
- * Provisioning is refused up front (see {@link assertAiGatewayProvisionable}) because the
52
- * gateway won't serve model requests until the account is on a paid plan. `upgradeUrl` is the
53
- * org-scoped Console billing page (see {@link aiGatewayUpgradeUrl}).
54
- */
55
- export const freePlanBlockMessage = (upgradeUrl) => "This neon.ts enables the AI Gateway, which isn't available on the Free plan — the " +
56
- "gateway won't serve model requests. Upgrade to a paid plan and re-run, or remove " +
57
- `\`preview.aiGateway\` from neon.ts. Upgrade here: ${upgradeUrl}`;
48
+ * The message shown when a `neon.ts` that enables the AI Gateway is applied on a Free plan.
49
+ * Provisioning is refused up front (see {@link assertAiGatewayProvisionable}) because the
50
+ * gateway won't serve model requests until the account is on a paid plan. `upgradeUrl` is the
51
+ * org-scoped Console billing page (see {@link aiGatewayUpgradeUrl}).
52
+ */
53
+ const freePlanBlockMessage = (upgradeUrl) => `This neon.ts enables the AI Gateway, which isn't available on the Free plan — the gateway won't serve model requests. Upgrade to a paid plan and re-run, or remove \`preview.aiGateway\` from neon.ts. Upgrade here: ${upgradeUrl}`;
58
54
  /**
59
- * Build the AI Gateway courtesy notice for an account's plan and (optionally) its live model
60
- * catalog, or `null` when nothing needs saying.
61
- *
62
- * `modelIds` is `undefined` when the catalog wasn't probed (e.g. a dry-run `plan`, or a probe
63
- * that failed); in that case only the plan-based (Free) notice can be produced — never a
64
- * false "reduced models" warning. `upgradeUrl` is the org-scoped Console billing page
65
- * (Free notice); `moreModelsUrl` is the branch's Console AI Gateway page
66
- * (see {@link aiGatewayModelsUrl}), shown when the catalog is reduced.
67
- */
68
- export const buildAiGatewayNotice = ({ subscriptionType, modelIds, upgradeUrl, moreModelsUrl, }) => {
69
- if (isFreePlan(subscriptionType)) {
70
- return {
71
- level: "warning",
72
- message: "AI Gateway is enabled, but the gateway does not serve model requests on the " +
73
- `Free plan. Upgrade to a paid plan to start making requests: ${upgradeUrl}`,
74
- };
75
- }
76
- if (modelIds !== undefined &&
77
- modelIds.length > 0 &&
78
- !hasFlagshipModels(modelIds)) {
79
- return {
80
- level: "warning",
81
- message: "AI Gateway is in public beta and not every model is enabled for your account " +
82
- "yet, so some models are missing from the catalog. Request access to more " +
83
- `models here: ${moreModelsUrl}`,
84
- };
85
- }
86
- return null;
55
+ * Build the AI Gateway courtesy notice for an account's plan and (optionally) its live model
56
+ * catalog, or `null` when nothing needs saying.
57
+ *
58
+ * `modelIds` is `undefined` when the catalog wasn't probed (e.g. a dry-run `plan`, or a probe
59
+ * that failed); in that case only the plan-based (Free) notice can be produced — never a
60
+ * false "reduced models" warning. `upgradeUrl` is the org-scoped Console billing page
61
+ * (Free notice); `moreModelsUrl` is the branch's Console AI Gateway page
62
+ * (see {@link aiGatewayModelsUrl}), shown when the catalog is reduced.
63
+ */
64
+ const buildAiGatewayNotice = ({ subscriptionType, modelIds, upgradeUrl, moreModelsUrl }) => {
65
+ if (isFreePlan(subscriptionType)) return {
66
+ level: "warning",
67
+ message: `AI Gateway is enabled, but the gateway does not serve model requests on the Free plan. Upgrade to a paid plan to start making requests: ${upgradeUrl}`
68
+ };
69
+ if (modelIds !== void 0 && modelIds.length > 0 && !hasFlagshipModels(modelIds)) return {
70
+ level: "warning",
71
+ message: `AI Gateway is in public beta and not every model is enabled for your account yet, so some models are missing from the catalog. Request access to more models here: ${moreModelsUrl}`
72
+ };
73
+ return null;
87
74
  };
88
75
  const isRecord = (value) => typeof value === "object" && value !== null;
89
76
  /**
90
- * Pull the `id`s of enabled models out of an OpenAI-compatible
91
- * `{ object: "list", data: [{ id, enabled, ... }] }` body. The gateway lists every model in
92
- * the catalog but marks ones the account can't serve yet with `enabled: false`; only the
93
- * enabled set is used to detect a reduced catalog.
94
- */
95
- export const extractEnabledModelIds = (body) => {
96
- if (!isRecord(body) || !Array.isArray(body.data))
97
- return null;
98
- const ids = [];
99
- for (const entry of body.data) {
100
- if (isRecord(entry) &&
101
- typeof entry.id === "string" &&
102
- entry.enabled === true) {
103
- ids.push(entry.id);
104
- }
105
- }
106
- return ids;
77
+ * Pull the `id`s of enabled models out of an OpenAI-compatible
78
+ * `{ object: "list", data: [{ id, enabled, ... }] }` body. The gateway lists every model in
79
+ * the catalog but marks ones the account can't serve yet with `enabled: false`; only the
80
+ * enabled set is used to detect a reduced catalog.
81
+ */
82
+ const extractEnabledModelIds = (body) => {
83
+ if (!isRecord(body) || !Array.isArray(body.data)) return null;
84
+ const ids = [];
85
+ for (const entry of body.data) if (isRecord(entry) && typeof entry.id === "string" && entry.enabled === true) ids.push(entry.id);
86
+ return ids;
107
87
  };
108
88
  /**
109
- * `GET {baseUrl}/v1/models` → the enabled model ids, or `null` if the catalog can't be read
110
- * (network / HTTP / parse failure). Returning `null` keeps the notice silent rather than
111
- * risking a false "reduced models" warning. `/v1/models` is served only on the unified
112
- * dialect (the `/openai/v1` Responses dialect returns 404).
113
- */
114
- export const fetchGatewayModelIds = async (baseUrl, token) => {
115
- try {
116
- const res = await fetch(`${baseUrl.replace(/\/+$/, "")}/v1/models`, {
117
- headers: { Authorization: `Bearer ${token}` },
118
- });
119
- if (!res.ok)
120
- return null;
121
- return extractEnabledModelIds(await res.json());
122
- }
123
- catch {
124
- return null;
125
- }
89
+ * `GET {baseUrl}/v1/models` → the enabled model ids, or `null` if the catalog can't be read
90
+ * (network / HTTP / parse failure). Returning `null` keeps the notice silent rather than
91
+ * risking a false "reduced models" warning. `/v1/models` is served only on the unified
92
+ * dialect (the `/openai/v1` Responses dialect returns 404).
93
+ */
94
+ const fetchGatewayModelIds = async (baseUrl, token) => {
95
+ try {
96
+ const res = await fetch(`${baseUrl.replace(/\/+$/, "")}/v1/models`, { headers: { Authorization: `Bearer ${token}` } });
97
+ if (!res.ok) return null;
98
+ return extractEnabledModelIds(await res.json());
99
+ } catch {
100
+ return null;
101
+ }
126
102
  };
127
103
  /**
128
- * Refuse to provision the AI Gateway on a Free plan. Called before the `neon.ts` lifecycle
129
- * commands provision a branch (`config apply` / `deploy`, `checkout`), so a Free-plan user
130
- * gets a clear "upgrade first" error instead of a credential that can't serve requests.
131
- *
132
- * Best-effort on the plan lookup: if the plan can't be determined (network / API failure) we
133
- * do NOT block, so a transient error never wrongly refuses a paid user's deploy. Only a
134
- * positively-identified Free plan throws.
135
- */
136
- export const assertAiGatewayProvisionable = async (params) => {
137
- let subscriptionType;
138
- let orgId;
139
- try {
140
- const { data } = await params.apiClient.getProject(params.projectId);
141
- subscriptionType = data.project.owner?.subscription_type;
142
- orgId = data.project.org_id ?? undefined;
143
- }
144
- catch {
145
- return; // Can't determine the plan — don't block.
146
- }
147
- if (isFreePlan(subscriptionType)) {
148
- throw new Error(freePlanBlockMessage(aiGatewayUpgradeUrl(orgId)));
149
- }
104
+ * Refuse to provision the AI Gateway on a Free plan. Called before the `neon.ts` lifecycle
105
+ * commands provision a branch (`config apply` / `deploy`, `checkout`), so a Free-plan user
106
+ * gets a clear "upgrade first" error instead of a credential that can't serve requests.
107
+ *
108
+ * Best-effort on the plan lookup: if the plan can't be determined (network / API failure) we
109
+ * do NOT block, so a transient error never wrongly refuses a paid user's deploy. Only a
110
+ * positively-identified Free plan throws.
111
+ */
112
+ const assertAiGatewayProvisionable = async (params) => {
113
+ let subscriptionType;
114
+ let orgId;
115
+ try {
116
+ const { data } = await params.apiClient.getProject(params.projectId);
117
+ subscriptionType = data.project.owner?.subscription_type;
118
+ orgId = data.project.org_id ?? void 0;
119
+ } catch {
120
+ return;
121
+ }
122
+ if (isFreePlan(subscriptionType)) throw new Error(freePlanBlockMessage(aiGatewayUpgradeUrl(orgId)));
150
123
  };
151
124
  /**
152
- * Resolve the account's plan (and, when gateway credentials are on hand, its live model
153
- * catalog) and print the AI Gateway courtesy notice — used by the `neon.ts` lifecycle and
154
- * `env pull` whenever a branch has the gateway enabled.
155
- *
156
- * Pass `gateway` (base URL + token) to enable the reduced-model-set check; omit it (e.g. a
157
- * dry-run `plan`) to get only the Free-plan notice. This is best-effort: any failure while
158
- * fetching the plan or catalog is swallowed so it can never break the underlying command.
159
- */
160
- export const warnAiGateway = async (params) => {
161
- try {
162
- const { data } = await params.apiClient.getProject(params.projectId);
163
- const subscriptionType = data.project.owner?.subscription_type;
164
- const orgId = data.project.org_id ?? undefined;
165
- const modelIds = params.gateway
166
- ? ((await fetchGatewayModelIds(params.gateway.baseUrl, params.gateway.token)) ?? undefined)
167
- : undefined;
168
- const notice = buildAiGatewayNotice({
169
- subscriptionType,
170
- modelIds,
171
- upgradeUrl: aiGatewayUpgradeUrl(orgId),
172
- moreModelsUrl: aiGatewayModelsUrl(params.projectId, params.branchId),
173
- });
174
- if (notice)
175
- log.warning(notice.message);
176
- }
177
- catch {
178
- // A courtesy notice must never break the command that triggered it.
179
- }
125
+ * Resolve the account's plan (and, when gateway credentials are on hand, its live model
126
+ * catalog) and print the AI Gateway courtesy notice — used by the `neon.ts` lifecycle and
127
+ * `env pull` whenever a branch has the gateway enabled.
128
+ *
129
+ * Pass `gateway` (base URL + token) to enable the reduced-model-set check; omit it (e.g. a
130
+ * dry-run `plan`) to get only the Free-plan notice. This is best-effort: any failure while
131
+ * fetching the plan or catalog is swallowed so it can never break the underlying command.
132
+ */
133
+ const warnAiGateway = async (params) => {
134
+ try {
135
+ const { data } = await params.apiClient.getProject(params.projectId);
136
+ const subscriptionType = data.project.owner?.subscription_type;
137
+ const orgId = data.project.org_id ?? void 0;
138
+ const modelIds = params.gateway ? await fetchGatewayModelIds(params.gateway.baseUrl, params.gateway.token) ?? void 0 : void 0;
139
+ const notice = buildAiGatewayNotice({
140
+ subscriptionType,
141
+ modelIds,
142
+ upgradeUrl: aiGatewayUpgradeUrl(orgId),
143
+ moreModelsUrl: aiGatewayModelsUrl(params.projectId, params.branchId)
144
+ });
145
+ if (notice) log.warning(notice.message);
146
+ } catch {}
180
147
  };
148
+ //#endregion
149
+ export { aiGatewayModelsUrl, aiGatewayUpgradeUrl, assertAiGatewayProvisionable, buildAiGatewayNotice, extractEnabledModelIds, fetchGatewayModelIds, freePlanBlockMessage, hasFlagshipModels, isFreePlan, warnAiGateway };
@@ -1,33 +1,26 @@
1
- // Runtime enum-like constants for Neon API string unions.
2
- //
3
- // `@neondatabase/api-client` generated TypeScript `enum`s (real runtime objects)
4
- // for fields like the compute endpoint type. `@neon/sdk` instead models these as
5
- // plain string-literal union *types*, which have no runtime value — so code that
6
- // read `EndpointType.ReadWrite` or `Object.values(EndpointType)` no longer works.
7
- //
8
- // These `as const` objects restore that runtime surface, and each is paired with
9
- // a same-named type whose union is identical to the corresponding `@neon/sdk`
10
- // type, so values stay assignable in both directions.
11
- export const EndpointType = {
12
- ReadOnly: "read_only",
13
- ReadWrite: "read_write",
1
+ //#region src/utils/api_enums.ts
2
+ const EndpointType = {
3
+ ReadOnly: "read_only",
4
+ ReadWrite: "read_write"
14
5
  };
15
- export const NeonAuthOauthProviderId = {
16
- Google: "google",
17
- Github: "github",
18
- Microsoft: "microsoft",
19
- Vercel: "vercel",
6
+ const NeonAuthOauthProviderId = {
7
+ Google: "google",
8
+ Github: "github",
9
+ Microsoft: "microsoft",
10
+ Vercel: "vercel"
20
11
  };
21
- export const NeonAuthOauthProviderType = {
22
- Standard: "standard",
23
- Shared: "shared",
12
+ const NeonAuthOauthProviderType = {
13
+ Standard: "standard",
14
+ Shared: "shared"
24
15
  };
25
- export const NeonAuthSupportedAuthProvider = {
26
- Mock: "mock",
27
- Stack: "stack",
28
- BetterAuth: "better_auth",
16
+ const NeonAuthSupportedAuthProvider = {
17
+ Mock: "mock",
18
+ Stack: "stack",
19
+ BetterAuth: "better_auth"
29
20
  };
30
- export const NeonAuthEmailVerificationMethod = {
31
- Link: "link",
32
- Otp: "otp",
21
+ const NeonAuthEmailVerificationMethod = {
22
+ Link: "link",
23
+ Otp: "otp"
33
24
  };
25
+ //#endregion
26
+ export { EndpointType, NeonAuthEmailVerificationMethod, NeonAuthOauthProviderId, NeonAuthOauthProviderType, NeonAuthSupportedAuthProvider };
@@ -1,5 +1,11 @@
1
- export const extendTokenSet = (tokenSet) => {
2
- const exp = new Date();
3
- exp.setSeconds(exp.getSeconds() + (tokenSet.expires_in ?? 0));
4
- return { ...tokenSet, expires_at: exp.getTime() };
1
+ //#region src/utils/auth.ts
2
+ const extendTokenSet = (tokenSet) => {
3
+ const exp = /* @__PURE__ */ new Date();
4
+ exp.setSeconds(exp.getSeconds() + (tokenSet.expires_in ?? 0));
5
+ return {
6
+ ...tokenSet,
7
+ expires_at: exp.getTime()
8
+ };
5
9
  };
10
+ //#endregion
11
+ export { extendTokenSet };
@@ -1,22 +1,23 @@
1
- import chalk from "chalk";
2
1
  import { log } from "../log.js";
2
+ import chalk from "chalk";
3
+ //#region src/utils/branch_notice.ts
3
4
  /**
4
- * Print a one-line "this command is targeting <branch>" notice to **stderr** so
5
- * the user can sanity-check they're acting on the branch they think they are —
6
- * before a `status` / `plan` / `apply` / `env pull` does its work. This is the
7
- * cheap guardrail that catches "I planned against the wrong branch" / "I pulled
8
- * env from the wrong branch" before it bites.
9
- *
10
- * - Skipped for machine-readable output (`--output json|yaml`) so it never has
11
- * to be reasoned about by a script; it's stderr-only regardless, keeping
12
- * `--output table` stdout clean for piping too.
13
- * - `verb` is the leading phrase, e.g. `'Planning against branch'` →
14
- * `→ Planning against branch main (br-…)`.
15
- */
16
- export const announceTargetBranch = (props, branch, verb) => {
17
- if (props.output === "json" || props.output === "yaml") {
18
- return;
19
- }
20
- const suffix = branch.usedDefault ? chalk.dim(" · project default") : "";
21
- log.info("%s %s %s %s%s", chalk.dim("→"), verb, chalk.cyan.bold(branch.branchName), chalk.dim(`(${branch.branchId})`), suffix);
5
+ * Print a one-line "this command is targeting <branch>" notice to **stderr** so
6
+ * the user can sanity-check they're acting on the branch they think they are —
7
+ * before a `status` / `plan` / `apply` / `env pull` does its work. This is the
8
+ * cheap guardrail that catches "I planned against the wrong branch" / "I pulled
9
+ * env from the wrong branch" before it bites.
10
+ *
11
+ * - Skipped for machine-readable output (`--output json|yaml`) so it never has
12
+ * to be reasoned about by a script; it's stderr-only regardless, keeping
13
+ * `--output table` stdout clean for piping too.
14
+ * - `verb` is the leading phrase, e.g. `'Planning against branch'` →
15
+ * `→ Planning against branch main (br-…)`.
16
+ */
17
+ const announceTargetBranch = (props, branch, verb) => {
18
+ if (props.output === "json" || props.output === "yaml") return;
19
+ const suffix = branch.usedDefault ? chalk.dim(" · project default") : "";
20
+ log.info("%s %s %s %s%s", chalk.dim("→"), verb, chalk.cyan.bold(branch.branchName), chalk.dim(`(${branch.branchId})`), suffix);
22
21
  };
22
+ //#endregion
23
+ export { announceTargetBranch };
@@ -1,103 +1,97 @@
1
- import prompts from "prompts";
2
- import { retryOnLock } from "../api.js";
3
1
  import { isCi } from "../env.js";
4
2
  import { log } from "../log.js";
3
+ import { retryOnLock } from "../api.js";
5
4
  import { EndpointType } from "./api_enums.js";
5
+ import prompts from "prompts";
6
+ //#region src/utils/branch_picker.ts
6
7
  /** Sentinel `value` for the "create a new branch" choice (no branch id can collide). */
7
8
  const CREATE_BRANCH_CHOICE = Symbol("create-branch");
8
9
  /**
9
- * Render a branch's display name with the same word labels as `neonctl branch list`
10
- * (`[default]`, `[protected]`) instead of symbols, so the picker reads clearly.
11
- */
10
+ * Render a branch's display name with the same word labels as `neonctl branch list`
11
+ * (`[default]`, `[protected]`) instead of symbols, so the picker reads clearly.
12
+ */
12
13
  const branchLabel = (branch) => {
13
- const labels = [];
14
- if (branch.default) {
15
- labels.push("[default]");
16
- }
17
- if (branch.protected) {
18
- labels.push("[protected]");
19
- }
20
- labels.push(branch.name);
21
- return labels.join(" ");
14
+ const labels = [];
15
+ if (branch.default) labels.push("[default]");
16
+ if (branch.protected) labels.push("[protected]");
17
+ labels.push(branch.name);
18
+ return labels.join(" ");
22
19
  };
23
20
  /**
24
- * Prompt the user to pick a branch from `branches`, with a "+ Create a new branch…" option
25
- * pinned to the top (mirroring the project/org pickers). The default selection is the
26
- * project's default branch (the create option sits at index 0, so the default index is
27
- * offset by one).
28
- *
29
- * Throws `opts.nonInteractiveMessage` when there is no TTY (or in CI): the caller knows the
30
- * right guidance for its command, so the message is supplied rather than hard-coded here.
31
- */
32
- export const pickBranchInteractively = async (branches, opts) => {
33
- if (isCi() || !process.stdout.isTTY) {
34
- throw new Error(opts.nonInteractiveMessage);
35
- }
36
- const defaultBranchIndex = branches.findIndex((b) => b.default);
37
- const initial = defaultBranchIndex >= 0 ? defaultBranchIndex + 1 : 0;
38
- const { choice } = await prompts({
39
- type: "select",
40
- name: "choice",
41
- message: opts.message,
42
- choices: [
43
- { title: "+ Create a new branch…", value: CREATE_BRANCH_CHOICE },
44
- ...branches.map((b) => ({
45
- title: `${branchLabel(b)} (${b.id})`,
46
- value: b.id,
47
- })),
48
- ],
49
- initial,
50
- });
51
- if (choice === undefined) {
52
- throw new Error("Aborted: no branch selected.");
53
- }
54
- if (choice === CREATE_BRANCH_CHOICE) {
55
- return { kind: "create", name: await promptNewBranchName(branches) };
56
- }
57
- return { kind: "existing", branchId: choice };
21
+ * Prompt the user to pick a branch from `branches`, with a "+ Create a new branch…" option
22
+ * pinned to the top (mirroring the project/org pickers). The default selection is the
23
+ * project's default branch (the create option sits at index 0, so the default index is
24
+ * offset by one).
25
+ *
26
+ * Throws `opts.nonInteractiveMessage` when there is no TTY (or in CI): the caller knows the
27
+ * right guidance for its command, so the message is supplied rather than hard-coded here.
28
+ */
29
+ const pickBranchInteractively = async (branches, opts) => {
30
+ if (isCi() || !process.stdout.isTTY) throw new Error(opts.nonInteractiveMessage);
31
+ const defaultBranchIndex = branches.findIndex((b) => b.default);
32
+ const initial = defaultBranchIndex >= 0 ? defaultBranchIndex + 1 : 0;
33
+ const { choice } = await prompts({
34
+ type: "select",
35
+ name: "choice",
36
+ message: opts.message,
37
+ choices: [{
38
+ title: "+ Create a new branch…",
39
+ value: CREATE_BRANCH_CHOICE
40
+ }, ...branches.map((b) => ({
41
+ title: `${branchLabel(b)} (${b.id})`,
42
+ value: b.id
43
+ }))],
44
+ initial
45
+ });
46
+ if (choice === void 0) throw new Error("Aborted: no branch selected.");
47
+ if (choice === CREATE_BRANCH_CHOICE) return {
48
+ kind: "create",
49
+ name: await promptNewBranchName(branches)
50
+ };
51
+ return {
52
+ kind: "existing",
53
+ branchId: choice
54
+ };
58
55
  };
59
56
  /**
60
- * Prompt for a new branch name, rejecting empty input and names already taken on the
61
- * project (so we never silently select a different, pre-existing branch).
62
- */
63
- export const promptNewBranchName = async (branches) => {
64
- const existing = new Set(branches.map((b) => b.name));
65
- const { name } = await prompts({
66
- type: "text",
67
- name: "name",
68
- message: "New branch name:",
69
- validate: (value) => {
70
- const trimmed = value.trim();
71
- if (trimmed === "")
72
- return "Branch name cannot be empty.";
73
- if (existing.has(trimmed))
74
- return `A branch named "${trimmed}" already exists.`;
75
- return true;
76
- },
77
- });
78
- const trimmed = typeof name === "string" ? name.trim() : "";
79
- if (trimmed === "") {
80
- throw new Error("Aborted: no branch name provided.");
81
- }
82
- return trimmed;
57
+ * Prompt for a new branch name, rejecting empty input and names already taken on the
58
+ * project (so we never silently select a different, pre-existing branch).
59
+ */
60
+ const promptNewBranchName = async (branches) => {
61
+ const existing = new Set(branches.map((b) => b.name));
62
+ const { name } = await prompts({
63
+ type: "text",
64
+ name: "name",
65
+ message: "New branch name:",
66
+ validate: (value) => {
67
+ const trimmed = value.trim();
68
+ if (trimmed === "") return "Branch name cannot be empty.";
69
+ if (existing.has(trimmed)) return `A branch named "${trimmed}" already exists.`;
70
+ return true;
71
+ }
72
+ });
73
+ const trimmed = typeof name === "string" ? name.trim() : "";
74
+ if (trimmed === "") throw new Error("Aborted: no branch name provided.");
75
+ return trimmed;
83
76
  };
84
77
  /**
85
- * Create a branch with the same defaults as `neonctl branch create --name <name>`:
86
- * branched from the project's default branch with a read-write compute endpoint. Returns
87
- * the new branch id.
88
- */
89
- export const createBranch = async (apiClient, projectId, name, branches) => {
90
- const defaultBranch = branches.find((b) => b.default);
91
- if (!defaultBranch) {
92
- throw new Error("No default branch found");
93
- }
94
- const { data } = await retryOnLock(() => apiClient.createProjectBranch(projectId, {
95
- branch: { name, parent_id: defaultBranch.id },
96
- endpoints: [{ type: EndpointType.ReadWrite }],
97
- }));
98
- if (defaultBranch.protected) {
99
- log.warning("The parent branch is protected; a unique role password has been generated for the new branch.");
100
- }
101
- log.info("Created branch %s (%s).", data.branch.name, data.branch.id);
102
- return data.branch.id;
78
+ * Create a branch with the same defaults as `neonctl branch create --name <name>`:
79
+ * branched from the project's default branch with a read-write compute endpoint. Returns
80
+ * the new branch id.
81
+ */
82
+ const createBranch = async (apiClient, projectId, name, branches) => {
83
+ const defaultBranch = branches.find((b) => b.default);
84
+ if (!defaultBranch) throw new Error("No default branch found");
85
+ const { data } = await retryOnLock(() => apiClient.createProjectBranch(projectId, {
86
+ branch: {
87
+ name,
88
+ parent_id: defaultBranch.id
89
+ },
90
+ endpoints: [{ type: EndpointType.ReadWrite }]
91
+ }));
92
+ if (defaultBranch.protected) log.warning("The parent branch is protected; a unique role password has been generated for the new branch.");
93
+ log.info("Created branch %s (%s).", data.branch.name, data.branch.id);
94
+ return data.branch.id;
103
95
  };
96
+ //#endregion
97
+ export { createBranch, pickBranchInteractively, promptNewBranchName };