neonctl 2.28.0 → 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 (135) hide show
  1. package/README.md +2 -2
  2. package/dist/analytics.js +35 -33
  3. package/dist/api.js +34 -34
  4. package/dist/auth.js +50 -44
  5. package/dist/cli.js +2 -2
  6. package/dist/commands/auth.js +58 -52
  7. package/dist/commands/bootstrap.js +115 -157
  8. package/dist/commands/branches.js +154 -147
  9. package/dist/commands/bucket.js +124 -118
  10. package/dist/commands/checkout.js +49 -49
  11. package/dist/commands/config.js +212 -88
  12. package/dist/commands/connection_string.js +62 -62
  13. package/dist/commands/data_api.js +96 -96
  14. package/dist/commands/databases.js +23 -23
  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 +97 -98
  19. package/dist/commands/index.js +26 -26
  20. package/dist/commands/init.js +23 -22
  21. package/dist/commands/ip_allow.js +29 -29
  22. package/dist/commands/link.js +223 -166
  23. package/dist/commands/neon_auth.js +381 -363
  24. package/dist/commands/operations.js +11 -11
  25. package/dist/commands/orgs.js +8 -8
  26. package/dist/commands/projects.js +101 -99
  27. package/dist/commands/psql.js +31 -31
  28. package/dist/commands/roles.js +21 -21
  29. package/dist/commands/schema_diff.js +23 -23
  30. package/dist/commands/set_context.js +17 -17
  31. package/dist/commands/status.js +17 -17
  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 +23 -16
  37. package/dist/current_branch_fast_path.js +6 -6
  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 +19 -19
  45. package/dist/functions_api.js +10 -10
  46. package/dist/help.js +15 -15
  47. package/dist/index.js +94 -92
  48. package/dist/log.js +2 -2
  49. package/dist/pkg.js +5 -5
  50. package/dist/psql/cli.js +4 -2
  51. package/dist/psql/command/cmd_cond.js +61 -61
  52. package/dist/psql/command/cmd_connect.js +159 -154
  53. package/dist/psql/command/cmd_copy.js +107 -97
  54. package/dist/psql/command/cmd_describe.js +368 -363
  55. package/dist/psql/command/cmd_format.js +276 -263
  56. package/dist/psql/command/cmd_io.js +269 -263
  57. package/dist/psql/command/cmd_lo.js +74 -66
  58. package/dist/psql/command/cmd_meta.js +148 -148
  59. package/dist/psql/command/cmd_misc.js +17 -17
  60. package/dist/psql/command/cmd_pipeline.js +142 -135
  61. package/dist/psql/command/cmd_restrict.js +25 -25
  62. package/dist/psql/command/cmd_show.js +183 -168
  63. package/dist/psql/command/dispatch.js +26 -26
  64. package/dist/psql/command/shared.js +14 -14
  65. package/dist/psql/complete/filenames.js +16 -16
  66. package/dist/psql/complete/index.js +4 -4
  67. package/dist/psql/complete/matcher.js +33 -32
  68. package/dist/psql/complete/psqlVars.js +173 -173
  69. package/dist/psql/complete/queries.js +5 -3
  70. package/dist/psql/complete/rules.js +900 -863
  71. package/dist/psql/core/common.js +136 -133
  72. package/dist/psql/core/help.js +343 -343
  73. package/dist/psql/core/mainloop.js +160 -153
  74. package/dist/psql/core/prompt.js +126 -123
  75. package/dist/psql/core/settings.js +111 -111
  76. package/dist/psql/core/sqlHelp.js +150 -150
  77. package/dist/psql/core/startup.js +211 -205
  78. package/dist/psql/core/syncVars.js +14 -14
  79. package/dist/psql/core/variables.js +24 -24
  80. package/dist/psql/describe/formatters.js +302 -289
  81. package/dist/psql/describe/processNamePattern.js +28 -28
  82. package/dist/psql/describe/queries.js +656 -651
  83. package/dist/psql/index.js +436 -411
  84. package/dist/psql/io/history.js +36 -36
  85. package/dist/psql/io/input.js +15 -15
  86. package/dist/psql/io/lineEditor/buffer.js +27 -25
  87. package/dist/psql/io/lineEditor/complete.js +15 -15
  88. package/dist/psql/io/lineEditor/filename.js +22 -22
  89. package/dist/psql/io/lineEditor/index.js +65 -62
  90. package/dist/psql/io/lineEditor/keymap.js +325 -318
  91. package/dist/psql/io/lineEditor/vt100.js +60 -60
  92. package/dist/psql/io/pgpass.js +18 -18
  93. package/dist/psql/io/pgservice.js +14 -14
  94. package/dist/psql/io/psqlrc.js +46 -46
  95. package/dist/psql/print/aligned.js +175 -166
  96. package/dist/psql/print/asciidoc.js +51 -51
  97. package/dist/psql/print/crosstab.js +34 -31
  98. package/dist/psql/print/csv.js +25 -22
  99. package/dist/psql/print/html.js +54 -54
  100. package/dist/psql/print/json.js +12 -12
  101. package/dist/psql/print/latex.js +118 -118
  102. package/dist/psql/print/pager.js +28 -26
  103. package/dist/psql/print/troff.js +48 -48
  104. package/dist/psql/print/unaligned.js +15 -14
  105. package/dist/psql/print/units.js +17 -17
  106. package/dist/psql/scanner/slash.js +48 -46
  107. package/dist/psql/scanner/sql.js +88 -84
  108. package/dist/psql/scanner/stringutils.js +21 -17
  109. package/dist/psql/types/index.js +7 -7
  110. package/dist/psql/types/scanner.js +8 -8
  111. package/dist/psql/wire/connection.js +341 -327
  112. package/dist/psql/wire/copy.js +7 -7
  113. package/dist/psql/wire/pipeline.js +26 -24
  114. package/dist/psql/wire/protocol.js +102 -102
  115. package/dist/psql/wire/sasl.js +62 -62
  116. package/dist/psql/wire/tls.js +79 -73
  117. package/dist/storage_api.js +15 -15
  118. package/dist/test_utils/fixtures.js +34 -31
  119. package/dist/test_utils/oauth_server.js +5 -5
  120. package/dist/utils/api_enums.js +13 -13
  121. package/dist/utils/branch_notice.js +5 -5
  122. package/dist/utils/branch_picker.js +26 -26
  123. package/dist/utils/compute_units.js +4 -4
  124. package/dist/utils/enrichers.js +20 -15
  125. package/dist/utils/esbuild.js +28 -28
  126. package/dist/utils/formats.js +1 -1
  127. package/dist/utils/middlewares.js +3 -3
  128. package/dist/utils/package_manager.js +68 -0
  129. package/dist/utils/point_in_time.js +12 -12
  130. package/dist/utils/psql.js +30 -30
  131. package/dist/utils/string.js +2 -2
  132. package/dist/utils/ui.js +9 -9
  133. package/dist/utils/zip.js +1 -1
  134. package/dist/writer.js +17 -17
  135. package/package.json +6 -7
@@ -1,26 +1,26 @@
1
- import { existsSync, readFileSync, writeFileSync, rmSync } from 'node:fs';
2
- import { join } from 'node:path';
3
- import { createHash } from 'node:crypto';
4
- import { getApiClient } from '../api.js';
5
- import { auth, refreshToken } from '../auth.js';
6
- import { isCurrentBranchProbe } from '../context.js';
7
- import { CREDENTIALS_FILE } from '../config.js';
8
- import { isCi } from '../env.js';
9
- import { log } from '../log.js';
10
- import { extendTokenSet } from '../utils/auth.js';
11
- export const command = 'auth';
12
- export const aliases = ['login'];
13
- export const describe = 'Authenticate';
14
- export const builder = (yargs) => yargs.option('context-file', {
1
+ import { createHash } from "node:crypto";
2
+ import { existsSync, readFileSync, rmSync, writeFileSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { getApiClient } from "../api.js";
5
+ import { auth, refreshToken } from "../auth.js";
6
+ import { CREDENTIALS_FILE } from "../config.js";
7
+ import { isConfigInit, isCurrentBranchProbe } from "../context.js";
8
+ import { isCi } from "../env.js";
9
+ import { log } from "../log.js";
10
+ import { extendTokenSet } from "../utils/auth.js";
11
+ export const command = "auth";
12
+ export const aliases = ["login"];
13
+ export const describe = "Authenticate";
14
+ export const builder = (yargs) => yargs.option("context-file", {
15
15
  hidden: true,
16
16
  });
17
17
  export const handler = async (args) => {
18
18
  await authFlow(args);
19
19
  };
20
- export const authFlow = async ({ configDir, oauthHost, clientId, apiHost, forceAuth, 'force-auth': forceAuthKebab, allowUnsafeTls, }) => {
20
+ export const authFlow = async ({ configDir, oauthHost, clientId, apiHost, forceAuth, "force-auth": forceAuthKebab, allowUnsafeTls, }) => {
21
21
  const allowInteractiveAuth = forceAuth ?? forceAuthKebab;
22
22
  if (!allowInteractiveAuth && isCi()) {
23
- throw new Error('Cannot run interactive auth in CI');
23
+ throw new Error("Cannot run interactive auth in CI");
24
24
  }
25
25
  const tokenSet = await auth({
26
26
  oauthHost: oauthHost,
@@ -30,16 +30,16 @@ export const authFlow = async ({ configDir, oauthHost, clientId, apiHost, forceA
30
30
  const credentialsPath = join(configDir, CREDENTIALS_FILE);
31
31
  try {
32
32
  await preserveCredentials(credentialsPath, tokenSet, getApiClient({
33
- apiKey: tokenSet.access_token || '',
33
+ apiKey: tokenSet.access_token || "",
34
34
  apiHost,
35
35
  }));
36
36
  }
37
37
  catch {
38
- log.error('Failed to save credentials');
39
- return '';
38
+ log.error("Failed to save credentials");
39
+ return "";
40
40
  }
41
- log.info('Auth complete');
42
- return tokenSet.access_token || '';
41
+ log.info("Auth complete");
42
+ return tokenSet.access_token || "";
43
43
  };
44
44
  const preserveCredentials = async (path, credentials, apiClient) => {
45
45
  const { data: { id }, } = await apiClient.getCurrentUserInfo();
@@ -52,13 +52,13 @@ const preserveCredentials = async (path, credentials, apiClient) => {
52
52
  writeFileSync(path, contents, {
53
53
  mode: 0o700,
54
54
  });
55
- log.debug('Saved credentials to %s', path);
56
- log.debug('Credentials MD5 hash: %s', md5hash(contents));
55
+ log.debug("Saved credentials to %s", path);
56
+ log.debug("Credentials MD5 hash: %s", md5hash(contents));
57
57
  };
58
58
  const handleExistingToken = async (tokenSet, props, credentialsPath) => {
59
59
  // Use existing access_token, if present and valid
60
60
  if (tokenSet.access_token && tokenSet.expires_at > Date.now()) {
61
- log.debug('Using existing valid access_token');
61
+ log.debug("Using existing valid access_token");
62
62
  const apiClient = getApiClient({
63
63
  apiKey: tokenSet.access_token,
64
64
  apiHost: props.apiHost,
@@ -67,10 +67,10 @@ const handleExistingToken = async (tokenSet, props, credentialsPath) => {
67
67
  }
68
68
  // Either access_token is missing or its expired. Refresh the token
69
69
  log.debug(tokenSet.expires_at < Date.now()
70
- ? 'Token is expired, attempting refresh'
71
- : 'Token is missing access_token, attempting refresh');
70
+ ? "Token is expired, attempting refresh"
71
+ : "Token is missing access_token, attempting refresh");
72
72
  if (!tokenSet.refresh_token) {
73
- log.debug('TokenSet is missing refresh_token, starting authentication');
73
+ log.debug("TokenSet is missing refresh_token, starting authentication");
74
74
  return null;
75
75
  }
76
76
  try {
@@ -87,13 +87,13 @@ const handleExistingToken = async (tokenSet, props, credentialsPath) => {
87
87
  apiHost: props.apiHost,
88
88
  });
89
89
  await preserveCredentials(credentialsPath, extendedTokenSet, apiClient);
90
- log.debug('Token refresh successful');
90
+ log.debug("Token refresh successful");
91
91
  return { apiKey, apiClient };
92
92
  }
93
93
  catch (err) {
94
- const typedErr = err instanceof Error ? err : new Error('Unknown error');
95
- log.debug('Failed to refresh token: %s', typedErr.message);
96
- throw new Error('AUTH_REFRESH_FAILED');
94
+ const typedErr = err instanceof Error ? err : new Error("Unknown error");
95
+ log.debug("Failed to refresh token: %s", typedErr.message);
96
+ throw new Error("AUTH_REFRESH_FAILED");
97
97
  }
98
98
  };
99
99
  export const ensureAuth = async (props) => {
@@ -107,23 +107,28 @@ export const ensureAuth = async (props) => {
107
107
  if (isCurrentBranchProbe(props)) {
108
108
  return;
109
109
  }
110
+ // `config init` only scaffolds a neon.ts and installs npm packages locally; it
111
+ // never calls the Neon API, so skip auth entirely — no token refresh, no login.
112
+ if (isConfigInit(props)) {
113
+ return;
114
+ }
110
115
  // `dev` runs a function locally. It injects the selected branch's env vars
111
116
  // when credentials happen to be available, but must never trigger an
112
117
  // interactive login: use an API key or existing stored credentials if
113
118
  // present, otherwise run with no API client (env injection is skipped).
114
- const isLocalDev = props._[0] === 'dev';
119
+ const isLocalDev = props._[0] === "dev";
115
120
  // `bootstrap` only copies a public template repo; it never calls the Neon
116
121
  // API, so it must work without credentials and must never pop a browser
117
122
  // login. It uses an API key / stored credentials when present (harmless),
118
123
  // otherwise it proceeds with no API client.
119
- const isBootstrap = props._[0] === 'bootstrap';
124
+ const isBootstrap = props._[0] === "bootstrap";
120
125
  // `init` manages its own auth flow (asks the user if they have an account,
121
126
  // then triggers OAuth at the right time). Skip the global auth middleware.
122
- const isInit = props._[0] === 'init';
127
+ const isInit = props._[0] === "init";
123
128
  // Use existing API key or handle auth command
124
- if (props.apiKey || props._[0] === 'auth') {
129
+ if (props.apiKey || props._[0] === "auth") {
125
130
  if (props.apiKey) {
126
- log.debug('Using an API key to authorize requests');
131
+ log.debug("Using an API key to authorize requests");
127
132
  }
128
133
  props.apiClient = getApiClient({
129
134
  apiKey: props.apiKey,
@@ -134,10 +139,10 @@ export const ensureAuth = async (props) => {
134
139
  const credentialsPath = join(props.configDir, CREDENTIALS_FILE);
135
140
  // Handle case when credentials file exists
136
141
  if (existsSync(credentialsPath)) {
137
- log.debug('Trying to read credentials from %s', credentialsPath);
142
+ log.debug("Trying to read credentials from %s", credentialsPath);
138
143
  try {
139
- const contents = readFileSync(credentialsPath, 'utf8');
140
- log.debug('Credentials MD5 hash: %s', md5hash(contents));
144
+ const contents = readFileSync(credentialsPath, "utf8");
145
+ log.debug("Credentials MD5 hash: %s", md5hash(contents));
141
146
  const tokenSet = JSON.parse(contents);
142
147
  // Try to use existing token or refresh it
143
148
  const result = await handleExistingToken(tokenSet, props, credentialsPath);
@@ -148,32 +153,33 @@ export const ensureAuth = async (props) => {
148
153
  }
149
154
  }
150
155
  catch (err) {
151
- if (!(err instanceof Error && err.message === 'AUTH_REFRESH_FAILED') &&
152
- err.code !== 'ENOENT' &&
156
+ if (!(err instanceof Error &&
157
+ err.message === "AUTH_REFRESH_FAILED") &&
158
+ err.code !== "ENOENT" &&
153
159
  !(err instanceof SyntaxError)) {
154
160
  // Throw for any errors except auth refresh failure, missing file, or invalid credentials file
155
161
  throw err;
156
162
  }
157
163
  // Fall through to new auth flow for auth failures
158
- log.debug('Ensure auth failed, starting authentication', err);
164
+ log.debug("Ensure auth failed, starting authentication", err);
159
165
  }
160
166
  }
161
167
  else {
162
- log.debug('Credentials file %s does not exist, starting authentication', credentialsPath);
168
+ log.debug("Credentials file %s does not exist, starting authentication", credentialsPath);
163
169
  }
164
170
  // `dev` never launches the interactive browser flow. With no usable
165
171
  // credentials it proceeds without an API client; env injection is skipped
166
172
  // and the function still runs locally.
167
173
  if (isLocalDev) {
168
- log.debug('dev: no usable credentials; running without env injection');
174
+ log.debug("dev: no usable credentials; running without env injection");
169
175
  return;
170
176
  }
171
177
  if (isBootstrap) {
172
- log.debug('bootstrap: no usable credentials; continuing without auth');
178
+ log.debug("bootstrap: no usable credentials; continuing without auth");
173
179
  return;
174
180
  }
175
181
  if (isInit) {
176
- log.debug('init: skipping global auth; init manages its own auth flow');
182
+ log.debug("init: skipping global auth; init manages its own auth flow");
177
183
  return;
178
184
  }
179
185
  // Start new auth flow if no valid token exists or refresh failed
@@ -193,16 +199,16 @@ export const deleteCredentials = (configDir) => {
193
199
  try {
194
200
  if (existsSync(credentialsPath)) {
195
201
  rmSync(credentialsPath);
196
- log.info('Deleted credentials from %s', credentialsPath);
202
+ log.info("Deleted credentials from %s", credentialsPath);
197
203
  }
198
204
  else {
199
- log.debug('Credentials file %s does not exist', credentialsPath);
205
+ log.debug("Credentials file %s does not exist", credentialsPath);
200
206
  }
201
207
  }
202
208
  catch (err) {
203
- const typedErr = err instanceof Error ? err : new Error('Unknown error');
204
- log.error('Failed to delete credentials: %s', typedErr.message);
205
- throw new Error('CREDENTIALS_DELETE_FAILED');
209
+ const typedErr = err instanceof Error ? err : new Error("Unknown error");
210
+ log.error("Failed to delete credentials: %s", typedErr.message);
211
+ throw new Error("CREDENTIALS_DELETE_FAILED");
206
212
  }
207
213
  };
208
- const md5hash = (s) => createHash('md5').update(s).digest('hex');
214
+ const md5hash = (s) => createHash("md5").update(s).digest("hex");