neonctl 2.27.1 → 2.29.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 (136) hide show
  1. package/README.md +35 -3
  2. package/dist/analytics.js +52 -34
  3. package/dist/api.js +643 -13
  4. package/dist/auth.js +50 -44
  5. package/dist/cli.js +8 -1
  6. package/dist/commands/auth.js +64 -51
  7. package/dist/commands/bootstrap.js +115 -157
  8. package/dist/commands/branches.js +160 -150
  9. package/dist/commands/bucket.js +183 -146
  10. package/dist/commands/checkout.js +51 -51
  11. package/dist/commands/config.js +228 -82
  12. package/dist/commands/connection_string.js +62 -62
  13. package/dist/commands/data_api.js +100 -101
  14. package/dist/commands/databases.js +29 -26
  15. package/dist/commands/deploy.js +12 -12
  16. package/dist/commands/dev.js +114 -114
  17. package/dist/commands/env.js +43 -43
  18. package/dist/commands/functions.js +101 -104
  19. package/dist/commands/index.js +27 -25
  20. package/dist/commands/init.js +23 -22
  21. package/dist/commands/ip_allow.js +29 -29
  22. package/dist/commands/link.js +232 -182
  23. package/dist/commands/neon_auth.js +385 -370
  24. package/dist/commands/operations.js +11 -11
  25. package/dist/commands/orgs.js +8 -8
  26. package/dist/commands/projects.js +103 -101
  27. package/dist/commands/psql.js +31 -31
  28. package/dist/commands/roles.js +27 -24
  29. package/dist/commands/schema_diff.js +25 -26
  30. package/dist/commands/set_context.js +17 -17
  31. package/dist/commands/status.js +40 -0
  32. package/dist/commands/user.js +5 -5
  33. package/dist/commands/vpc_endpoints.js +50 -50
  34. package/dist/config.js +7 -7
  35. package/dist/config_format.js +5 -5
  36. package/dist/context.js +37 -14
  37. package/dist/current_branch_fast_path.js +55 -0
  38. package/dist/dev/env.js +33 -33
  39. package/dist/dev/functions.js +4 -4
  40. package/dist/dev/inputs.js +6 -6
  41. package/dist/dev/runtime.js +25 -25
  42. package/dist/env.js +14 -14
  43. package/dist/env_file.js +13 -13
  44. package/dist/errors.js +68 -5
  45. package/dist/functions_api.js +10 -10
  46. package/dist/help.js +15 -15
  47. package/dist/index.js +110 -107
  48. package/dist/log.js +2 -2
  49. package/dist/parameters.gen.js +14 -14
  50. package/dist/pkg.js +5 -5
  51. package/dist/psql/cli.js +4 -2
  52. package/dist/psql/command/cmd_cond.js +61 -61
  53. package/dist/psql/command/cmd_connect.js +159 -154
  54. package/dist/psql/command/cmd_copy.js +107 -97
  55. package/dist/psql/command/cmd_describe.js +368 -363
  56. package/dist/psql/command/cmd_format.js +276 -263
  57. package/dist/psql/command/cmd_io.js +269 -263
  58. package/dist/psql/command/cmd_lo.js +74 -66
  59. package/dist/psql/command/cmd_meta.js +148 -148
  60. package/dist/psql/command/cmd_misc.js +17 -17
  61. package/dist/psql/command/cmd_pipeline.js +142 -135
  62. package/dist/psql/command/cmd_restrict.js +25 -25
  63. package/dist/psql/command/cmd_show.js +183 -168
  64. package/dist/psql/command/dispatch.js +26 -26
  65. package/dist/psql/command/shared.js +14 -14
  66. package/dist/psql/complete/filenames.js +16 -16
  67. package/dist/psql/complete/index.js +4 -4
  68. package/dist/psql/complete/matcher.js +33 -32
  69. package/dist/psql/complete/psqlVars.js +173 -173
  70. package/dist/psql/complete/queries.js +5 -3
  71. package/dist/psql/complete/rules.js +900 -863
  72. package/dist/psql/core/common.js +136 -133
  73. package/dist/psql/core/help.js +343 -343
  74. package/dist/psql/core/mainloop.js +160 -153
  75. package/dist/psql/core/prompt.js +126 -123
  76. package/dist/psql/core/settings.js +111 -111
  77. package/dist/psql/core/sqlHelp.js +150 -150
  78. package/dist/psql/core/startup.js +211 -205
  79. package/dist/psql/core/syncVars.js +14 -14
  80. package/dist/psql/core/variables.js +24 -24
  81. package/dist/psql/describe/formatters.js +302 -289
  82. package/dist/psql/describe/processNamePattern.js +28 -28
  83. package/dist/psql/describe/queries.js +656 -651
  84. package/dist/psql/index.js +436 -411
  85. package/dist/psql/io/history.js +36 -36
  86. package/dist/psql/io/input.js +15 -15
  87. package/dist/psql/io/lineEditor/buffer.js +27 -25
  88. package/dist/psql/io/lineEditor/complete.js +15 -15
  89. package/dist/psql/io/lineEditor/filename.js +22 -22
  90. package/dist/psql/io/lineEditor/index.js +65 -62
  91. package/dist/psql/io/lineEditor/keymap.js +325 -318
  92. package/dist/psql/io/lineEditor/vt100.js +60 -60
  93. package/dist/psql/io/pgpass.js +18 -18
  94. package/dist/psql/io/pgservice.js +14 -14
  95. package/dist/psql/io/psqlrc.js +46 -46
  96. package/dist/psql/print/aligned.js +175 -166
  97. package/dist/psql/print/asciidoc.js +51 -51
  98. package/dist/psql/print/crosstab.js +34 -31
  99. package/dist/psql/print/csv.js +25 -22
  100. package/dist/psql/print/html.js +54 -54
  101. package/dist/psql/print/json.js +12 -12
  102. package/dist/psql/print/latex.js +118 -118
  103. package/dist/psql/print/pager.js +28 -26
  104. package/dist/psql/print/troff.js +48 -48
  105. package/dist/psql/print/unaligned.js +15 -14
  106. package/dist/psql/print/units.js +17 -17
  107. package/dist/psql/scanner/slash.js +48 -46
  108. package/dist/psql/scanner/sql.js +88 -84
  109. package/dist/psql/scanner/stringutils.js +21 -17
  110. package/dist/psql/types/index.js +7 -7
  111. package/dist/psql/types/scanner.js +8 -8
  112. package/dist/psql/wire/connection.js +341 -327
  113. package/dist/psql/wire/copy.js +7 -7
  114. package/dist/psql/wire/pipeline.js +26 -24
  115. package/dist/psql/wire/protocol.js +102 -102
  116. package/dist/psql/wire/sasl.js +62 -62
  117. package/dist/psql/wire/tls.js +79 -73
  118. package/dist/storage_api.js +22 -23
  119. package/dist/test_utils/fixtures.js +74 -41
  120. package/dist/test_utils/oauth_server.js +5 -5
  121. package/dist/utils/api_enums.js +33 -0
  122. package/dist/utils/branch_notice.js +5 -5
  123. package/dist/utils/branch_picker.js +26 -26
  124. package/dist/utils/compute_units.js +4 -4
  125. package/dist/utils/enrichers.js +28 -16
  126. package/dist/utils/esbuild.js +28 -28
  127. package/dist/utils/formats.js +1 -1
  128. package/dist/utils/middlewares.js +3 -3
  129. package/dist/utils/package_manager.js +68 -0
  130. package/dist/utils/point_in_time.js +12 -12
  131. package/dist/utils/psql.js +30 -30
  132. package/dist/utils/string.js +2 -2
  133. package/dist/utils/ui.js +9 -9
  134. package/dist/utils/zip.js +1 -1
  135. package/dist/writer.js +17 -17
  136. package/package.json +10 -12
package/README.md CHANGED
@@ -361,7 +361,7 @@ neonctl env pull
361
361
  neonctl env pull --branch preview --file .env.preview
362
362
  ```
363
363
 
364
- If you'd rather not keep env vars on disk, inject them at runtime instead with `neon-env run -- <your dev command>` (from `@neondatabase/env`) or `neonctl dev`, and pass `--no-env-pull` to `link` / `checkout`.
364
+ If you'd rather not keep env vars on disk, inject them at runtime instead with `neon-env run -- <your dev command>` (from `@neon/env`) or `neonctl dev`, and pass `--no-env-pull` to `link` / `checkout`.
365
365
 
366
366
  **Where `.neon` lives**: `link` writes `.neon` into the **current working directory** by default. If an existing `.neon` is found in any parent directory, that file is reused — so commands run from a sub-directory of a linked project still pick up the project's context. To pin the location explicitly, pass `--context-file <path>`.
367
367
 
@@ -373,7 +373,7 @@ Describe a branch's desired state in a `neon.ts` policy and reconcile it from th
373
373
 
374
374
  ```ts
375
375
  // neon.ts
376
- import { defineConfig } from '@neondatabase/config/v1';
376
+ import { defineConfig } from '@neon/config/v1';
377
377
 
378
378
  export default defineConfig({
379
379
  // Static: what exists on every branch (drives the typed env).
@@ -388,12 +388,15 @@ export default defineConfig({
388
388
  });
389
389
  ```
390
390
 
391
- Three sub-commands plus a top-level alias drive it:
391
+ Three sub-commands plus two top-level aliases drive it:
392
392
 
393
393
  ```bash
394
394
  # Inspect the branch's live Neon state (read-only — never mutates)
395
395
  neon config status
396
396
 
397
+ # `neon status` is an alias for `neon config status`
398
+ neon status
399
+
397
400
  # Dry-run diff: show exactly what `apply` would change
398
401
  neon config plan
399
402
 
@@ -419,6 +422,35 @@ The branch is chosen with `--branch <id|name>`; without it the project's default
419
422
 
420
423
  **Output**: `status` prints the project, branch, and reverse-engineered config; `plan` / `apply` print the planned/applied changes and any conflicts as tables. Pass `--output json` (or `--output yaml`) to emit the full machine-readable result (`PushResult`) for piping into other tools or CI.
421
424
 
425
+ **`config status --current-branch`** (alias `neon status --current-branch`) prints _only_ the branch pinned in the local `.neon` file — no network, no auth, no analytics — and exits non-zero when none is pinned. This behavior lets it safely drive a shell prompt. Example [starship](https://starship.rs) segment:
426
+
427
+ ```toml
428
+ [custom.neon]
429
+ description = "Current Neon database branch"
430
+ format = "[$symbol$output]($style) "
431
+ style = "bold green"
432
+ # `symbol` below uses a Nerd Font glyph; swap it for a plain
433
+ # label/emoji if you don't have a Nerd Font installed.
434
+ symbol = " "
435
+ command = "neon status --current-branch"
436
+ # Starship evaluates this on EVERY prompt render. To keep prompts instant
437
+ # everywhere outside a Neon project, do a zero-subprocess walk-up for an
438
+ # ancestor `.neon` first (the same walk the CLI does, stopping at $HOME and /).
439
+ # Only when one is found do we invoke the CLI, whose exit code is the real
440
+ # gate: non-zero (no branch pinned) hides the segment cleanly.
441
+ when = '''
442
+ d="$PWD"
443
+ while [ "$d" != "$HOME" ] && [ "$d" != / ]; do
444
+ if [ -e "$d/.neon" ]; then
445
+ neon status --current-branch >/dev/null 2>&1
446
+ exit $?
447
+ fi
448
+ d=$(dirname "$d")
449
+ done
450
+ exit 1
451
+ '''
452
+ ```
453
+
422
454
  ```bash
423
455
  # CI gate: fail the build if the branch has drifted from the policy
424
456
  neon config plan --project-id polished-snowflake-12345678 --output json
package/dist/analytics.js CHANGED
@@ -1,16 +1,22 @@
1
- import { readFileSync } from 'node:fs';
2
- import { join } from 'node:path';
3
- import { Analytics } from '@segment/analytics-node';
4
- import { isAxiosError } from 'axios';
5
- import { CREDENTIALS_FILE } from './config.js';
6
- import { getGithubEnvVars, isCi } from './env.js';
7
- import { log } from './log.js';
8
- import pkg from './pkg.js';
9
- import { getApiClient } from './api.js';
10
- const WRITE_KEY = '3SQXn5ejjXWLEJ8xU2PRYhAotLtTaeeV';
1
+ import { readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { Analytics } from "@segment/analytics-node";
4
+ import { getApiClient, isNeonApiError } from "./api.js";
5
+ import { CREDENTIALS_FILE } from "./config.js";
6
+ import { isCurrentBranchProbe } from "./context.js";
7
+ import { getGithubEnvVars, isCi } from "./env.js";
8
+ import { log } from "./log.js";
9
+ import pkg from "./pkg.js";
10
+ const WRITE_KEY = "3SQXn5ejjXWLEJ8xU2PRYhAotLtTaeeV";
11
+ /**
12
+ * Raw-argv fallback for the offline `--current-branch` probe. The init
13
+ * middleware runs before validation, where the parsed `currentBranch` flag may
14
+ * not be populated yet, so we also scan `process.argv` directly to be safe.
15
+ */
16
+ const hasCurrentBranchArgv = () => process.argv.includes("--current-branch");
11
17
  let client;
12
18
  let clientInitialized = false;
13
- let userId = '';
19
+ let userId = "";
14
20
  /**
15
21
  * Phase 1: Run before validation so the Segment client exists if any
16
22
  * middleware (e.g. auth) fails. Enables sendError() in the fail handler.
@@ -20,14 +26,21 @@ export const initAnalyticsClientMiddleware = (args) => {
20
26
  if (!args.analytics || clientInitialized) {
21
27
  return;
22
28
  }
29
+ // The offline `--current-branch` probe must make zero network calls. This
30
+ // middleware runs before validation, so guard on the raw argv too (in case
31
+ // the parsed `currentBranch` flag isn't populated this early): never create
32
+ // the Segment client, which keeps trackEvent/closeAnalytics no-ops downstream.
33
+ if (isCurrentBranchProbe(args) || hasCurrentBranchArgv()) {
34
+ return;
35
+ }
23
36
  clientInitialized = true;
24
37
  client = new Analytics({
25
38
  writeKey: WRITE_KEY,
26
- host: 'https://track.neon.tech',
39
+ host: "https://track.neon.tech",
27
40
  });
28
- log.debug('Initialized CLI analytics client');
41
+ log.debug("Initialized CLI analytics client");
29
42
  client.identify({
30
- userId: 'anonymous',
43
+ userId: "anonymous",
31
44
  });
32
45
  };
33
46
  /**
@@ -38,13 +51,18 @@ export const analyticsMiddleware = async (args) => {
38
51
  if (!client || !args.analytics) {
39
52
  return;
40
53
  }
54
+ if (isCurrentBranchProbe(args)) {
55
+ return;
56
+ }
41
57
  try {
42
58
  const credentialsPath = join(args.configDir, CREDENTIALS_FILE);
43
- const credentials = readFileSync(credentialsPath, { encoding: 'utf-8' });
59
+ const credentials = readFileSync(credentialsPath, {
60
+ encoding: "utf-8",
61
+ });
44
62
  userId = JSON.parse(credentials).user_id;
45
63
  }
46
64
  catch (err) {
47
- log.debug('Failed to read credentials file', err);
65
+ log.debug("Failed to read credentials file", err);
48
66
  }
49
67
  try {
50
68
  if (args.apiKey) {
@@ -59,25 +77,25 @@ export const analyticsMiddleware = async (args) => {
59
77
  args.authMethod = authDetails.auth_method;
60
78
  args.authData = authDetails.auth_data;
61
79
  // Get user id if not org api key
62
- if (!userId && authDetails.auth_method !== 'api_key_org') {
80
+ if (!userId && authDetails.auth_method !== "api_key_org") {
63
81
  const resp = await apiClient?.getCurrentUserInfo?.();
64
82
  userId = resp?.data?.id;
65
83
  }
66
84
  }
67
85
  else {
68
86
  args.accountId = userId;
69
- args.authMethod = 'oauth';
87
+ args.authMethod = "oauth";
70
88
  }
71
89
  }
72
90
  catch (err) {
73
- log.debug('Failed to get user id from api', err);
91
+ log.debug("Failed to get user id from api", err);
74
92
  }
75
93
  client.identify({
76
- userId: userId?.toString() ?? 'anonymous',
94
+ userId: userId?.toString() ?? "anonymous",
77
95
  });
78
96
  client.track({
79
- userId: userId || 'anonymous',
80
- event: 'CLI Started',
97
+ userId: userId || "anonymous",
98
+ event: "CLI Started",
81
99
  properties: getAnalyticsEventProperties(args),
82
100
  context: {
83
101
  direct: true,
@@ -86,35 +104,35 @@ export const analyticsMiddleware = async (args) => {
86
104
  };
87
105
  export const closeAnalytics = async (opts) => {
88
106
  if (client) {
89
- log.debug('Flushing CLI analytics');
107
+ log.debug("Flushing CLI analytics");
90
108
  // `timeout` bounds how long we wait for in-flight events to flush so a
91
109
  // slow / unreachable track.neon.tech can't hang a short-lived command
92
110
  // (e.g. the psql launch path, which flushes here before process.exit).
93
111
  await client.closeAndFlush(opts);
94
- log.debug('Flushed CLI analytics');
112
+ log.debug("Flushed CLI analytics");
95
113
  }
96
114
  };
97
115
  export const sendError = (err, errCode) => {
98
116
  if (!client) {
99
117
  return;
100
118
  }
101
- const axiosError = isAxiosError(err) ? err : undefined;
102
- const requestId = axiosError?.response?.headers['x-neon-ret-request-id'];
119
+ const apiError = isNeonApiError(err) ? err : undefined;
120
+ const requestId = apiError?.headers?.["x-neon-ret-request-id"];
103
121
  if (requestId) {
104
- log.debug('Failed request ID: %s', requestId);
122
+ log.debug("Failed request ID: %s", requestId);
105
123
  }
106
124
  client.track({
107
- event: 'CLI Error',
108
- userId: userId || 'anonymous',
125
+ event: "CLI Error",
126
+ userId: userId || "anonymous",
109
127
  properties: {
110
128
  message: err.message,
111
129
  stack: err.stack,
112
130
  errCode,
113
- statusCode: axiosError?.response?.status,
131
+ statusCode: apiError?.status,
114
132
  requestId: requestId,
115
133
  },
116
134
  });
117
- log.debug('Sent CLI error event: %s', errCode);
135
+ log.debug("Sent CLI error event: %s", errCode);
118
136
  };
119
137
  export const trackEvent = (event, properties) => {
120
138
  if (!client) {
@@ -122,14 +140,14 @@ export const trackEvent = (event, properties) => {
122
140
  }
123
141
  client.track({
124
142
  event,
125
- userId: userId || 'anonymous',
143
+ userId: userId || "anonymous",
126
144
  properties,
127
145
  });
128
- log.debug('Sent CLI event: %s', event);
146
+ log.debug("Sent CLI event: %s", event);
129
147
  };
130
148
  export const getAnalyticsEventProperties = (args) => ({
131
149
  version: pkg.version,
132
- command: args._.join(' '),
150
+ command: args._.join(" "),
133
151
  flags: {
134
152
  output: args.output,
135
153
  },